Refactor device enabled_class to enum + expose in API.

This commit is contained in:
Orne Brocaar
2023-06-14 11:17:04 +01:00
parent fd29f4190b
commit a1a34abaf7
20 changed files with 893 additions and 752 deletions

View File

@ -16,520 +16,542 @@ import "google/protobuf/empty.proto";
// DeviceService is the service providing API methods for managing devices. // DeviceService is the service providing API methods for managing devices.
service DeviceService { service DeviceService {
// Create the given device. // Create the given device.
rpc Create(CreateDeviceRequest) returns (google.protobuf.Empty) { rpc Create(CreateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
post: "/api/devices" post : "/api/devices"
body: "*" body : "*"
}; };
} }
// Get returns the device for the given DevEUI. // Get returns the device for the given DevEUI.
rpc Get(GetDeviceRequest) returns (GetDeviceResponse) { rpc Get(GetDeviceRequest) returns (GetDeviceResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices/{dev_eui}" get : "/api/devices/{dev_eui}"
}; };
} }
// Update the given device. // Update the given device.
rpc Update(UpdateDeviceRequest) returns (google.protobuf.Empty) { rpc Update(UpdateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
put: "/api/devices/{device.dev_eui}" put : "/api/devices/{device.dev_eui}"
body: "*" body : "*"
}; };
} }
// Delete the device with the given DevEUI. // Delete the device with the given DevEUI.
rpc Delete(DeleteDeviceRequest) returns (google.protobuf.Empty) { rpc Delete(DeleteDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}" delete : "/api/devices/{dev_eui}"
}; };
} }
// Get the list of devices. // Get the list of devices.
rpc List(ListDevicesRequest) returns (ListDevicesResponse) { rpc List(ListDevicesRequest) returns (ListDevicesResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices" get : "/api/devices"
}; };
} }
// Create the given device-keys. // Create the given device-keys.
rpc CreateKeys(CreateDeviceKeysRequest) returns (google.protobuf.Empty) { rpc CreateKeys(CreateDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
post: "/api/devices/{device_keys.dev_eui}/keys" post : "/api/devices/{device_keys.dev_eui}/keys"
body: "*" body : "*"
}; };
} }
// Get the device-keys for the given DevEUI. // Get the device-keys for the given DevEUI.
rpc GetKeys(GetDeviceKeysRequest) returns (GetDeviceKeysResponse) { rpc GetKeys(GetDeviceKeysRequest) returns (GetDeviceKeysResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices/{dev_eui}/keys" get : "/api/devices/{dev_eui}/keys"
}; };
} }
// Update the given device-keys. // Update the given device-keys.
rpc UpdateKeys(UpdateDeviceKeysRequest) returns (google.protobuf.Empty) { rpc UpdateKeys(UpdateDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
put: "/api/devices/{device_keys.dev_eui}/keys" put : "/api/devices/{device_keys.dev_eui}/keys"
body: "*" body : "*"
}; };
} }
// Delete the device-keys for the given DevEUI. // Delete the device-keys for the given DevEUI.
rpc DeleteKeys(DeleteDeviceKeysRequest) returns (google.protobuf.Empty) { rpc DeleteKeys(DeleteDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/keys" delete : "/api/devices/{dev_eui}/keys"
}; };
} }
// FlushDevNonces flushes the OTAA device nonces. // FlushDevNonces flushes the OTAA device nonces.
rpc FlushDevNonces(FlushDevNoncesRequest) returns (google.protobuf.Empty) { rpc FlushDevNonces(FlushDevNoncesRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/dev-nonces" delete : "/api/devices/{dev_eui}/dev-nonces"
}; };
} }
// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations). // Activate (re)activates the device with the given parameters (for ABP or for
rpc Activate(ActivateDeviceRequest) returns (google.protobuf.Empty) { // importing OTAA activations).
option(google.api.http) = { rpc Activate(ActivateDeviceRequest) returns (google.protobuf.Empty) {
post: "/api/devices/{device_activation.dev_eui}/activate" option (google.api.http) = {
body: "*" post : "/api/devices/{device_activation.dev_eui}/activate"
}; body : "*"
} };
}
// Deactivate de-activates the device. // Deactivate de-activates the device.
rpc Deactivate(DeactivateDeviceRequest) returns (google.protobuf.Empty) { rpc Deactivate(DeactivateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/activation" delete : "/api/devices/{dev_eui}/activation"
}; };
} }
// GetActivation returns the current activation details of the device (OTAA or ABP). // GetActivation returns the current activation details of the device (OTAA or
rpc GetActivation(GetDeviceActivationRequest) returns (GetDeviceActivationResponse) { // ABP).
option(google.api.http) = { rpc GetActivation(GetDeviceActivationRequest)
get: "/api/devices/{dev_eui}/activation" returns (GetDeviceActivationResponse) {
}; option (google.api.http) = {
} get : "/api/devices/{dev_eui}/activation"
};
}
// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account. // GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into
rpc GetRandomDevAddr(GetRandomDevAddrRequest) returns (GetRandomDevAddrResponse) { // account.
option(google.api.http) = { rpc GetRandomDevAddr(GetRandomDevAddrRequest)
post: "/api/devices/{dev_eui}/get-random-dev-addr" returns (GetRandomDevAddrResponse) {
}; option (google.api.http) = {
} post : "/api/devices/{dev_eui}/get-random-dev-addr"
};
}
// GetMetrics returns the device metrics. // GetMetrics returns the device metrics.
// Note that this requires a device-profile with codec and measurements configured. // Note that this requires a device-profile with codec and measurements
rpc GetMetrics(GetDeviceMetricsRequest) returns (GetDeviceMetricsResponse) { // configured.
option(google.api.http) = { rpc GetMetrics(GetDeviceMetricsRequest) returns (GetDeviceMetricsResponse) {
get: "/api/devices/{dev_eui}/metrics" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/metrics"
} };
}
// GetLinkMetrics returns the device link metrics. // GetLinkMetrics returns the device link metrics.
// This includes uplinks, downlinks, RSSI, SNR, etc... // This includes uplinks, downlinks, RSSI, SNR, etc...
rpc GetLinkMetrics(GetDeviceLinkMetricsRequest) returns (GetDeviceLinkMetricsResponse) { rpc GetLinkMetrics(GetDeviceLinkMetricsRequest)
option(google.api.http) = { returns (GetDeviceLinkMetricsResponse) {
get: "/api/devices/{dev_eui}/link-metrics" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/link-metrics"
} };
}
// Enqueue adds the given item to the downlink queue. // Enqueue adds the given item to the downlink queue.
rpc Enqueue(EnqueueDeviceQueueItemRequest) returns (EnqueueDeviceQueueItemResponse) { rpc Enqueue(EnqueueDeviceQueueItemRequest)
option(google.api.http) = { returns (EnqueueDeviceQueueItemResponse) {
post: "/api/devices/{queue_item.dev_eui}/queue" option (google.api.http) = {
body: "*" post : "/api/devices/{queue_item.dev_eui}/queue"
}; body : "*"
} };
}
// FlushQueue flushes the downlink device-queue. // FlushQueue flushes the downlink device-queue.
rpc FlushQueue(FlushDeviceQueueRequest) returns (google.protobuf.Empty) { rpc FlushQueue(FlushDeviceQueueRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/queue" delete : "/api/devices/{dev_eui}/queue"
}; };
} }
// GetQueue returns the downlink device-queue. // GetQueue returns the downlink device-queue.
rpc GetQueue(GetDeviceQueueItemsRequest) returns (GetDeviceQueueItemsResponse) { rpc GetQueue(GetDeviceQueueItemsRequest)
option(google.api.http) = { returns (GetDeviceQueueItemsResponse) {
get: "/api/devices/{dev_eui}/queue" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/queue"
} };
}
}
enum DeviceClass {
// Class-A.
A = 0;
// Class-B.
B = 1;
// Class-C.
C = 2;
} }
message Device { message Device {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Name. // Name.
string name = 2; string name = 2;
// Description. // Description.
string description = 3; string description = 3;
// Application ID (UUID). // Application ID (UUID).
string application_id = 4; string application_id = 4;
// Device-profile ID (UUID). // Device-profile ID (UUID).
string device_profile_id = 5; string device_profile_id = 5;
// Skip frame-counter checks (this is insecure, but could be helpful for debugging). // Skip frame-counter checks (this is insecure, but could be helpful for
bool skip_fcnt_check = 6; // debugging).
bool skip_fcnt_check = 6;
// Device is disabled. // Device is disabled.
bool is_disabled = 7; bool is_disabled = 7;
// Variables (user defined). // Variables (user defined).
// These variables can be used together with integrations to store tokens / // These variables can be used together with integrations to store tokens /
// secrets that must be configured per device. These variables are not // secrets that must be configured per device. These variables are not
// exposed in the event payloads. // exposed in the event payloads.
map<string, string> variables = 8; map<string, string> variables = 8;
// Tags (user defined). // Tags (user defined).
// These tags are exposed in the event payloads or to integration. Tags are // These tags are exposed in the event payloads or to integration. Tags are
// intended for aggregation and filtering. // intended for aggregation and filtering.
map<string, string> tags = 9; map<string, string> tags = 9;
// JoinEUI (optional, EUI64). // JoinEUI (optional, EUI64).
// This field will be automatically set / updated on OTAA. However, in some // This field will be automatically set / updated on OTAA. However, in some
// cases it must be pre-configured. For example to allow OTAA using a Relay. // cases it must be pre-configured. For example to allow OTAA using a Relay.
// In this case the Relay needs to know the JoinEUI + DevEUI combinations // In this case the Relay needs to know the JoinEUI + DevEUI combinations
// of the devices for which it needs to forward uplinks. // of the devices for which it needs to forward uplinks.
string join_eui = 10; string join_eui = 10;
} }
message DeviceStatus { message DeviceStatus {
// The device margin status // The device margin status
// -32..32: The demodulation SNR ration in dB // -32..32: The demodulation SNR ration in dB
int32 margin = 1; int32 margin = 1;
// Device is connected to an external power source. // Device is connected to an external power source.
bool external_power_source = 2; bool external_power_source = 2;
// Device battery level as a percentage. // Device battery level as a percentage.
// -1 when the battery level is not available. // -1 when the battery level is not available.
float battery_level = 3; float battery_level = 3;
} }
message DeviceListItem { message DeviceListItem {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
// Last seen at timestamp. // Last seen at timestamp.
google.protobuf.Timestamp last_seen_at = 4; google.protobuf.Timestamp last_seen_at = 4;
// Name. // Name.
string name = 5; string name = 5;
// Description. // Description.
string description = 6; string description = 6;
// Device-profile ID (UUID). // Device-profile ID (UUID).
string device_profile_id = 7; string device_profile_id = 7;
// Device-profile name. // Device-profile name.
string device_profile_name = 8; string device_profile_name = 8;
// Device status. // Device status.
DeviceStatus device_status = 9; DeviceStatus device_status = 9;
} }
message DeviceKeys { message DeviceKeys {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Network root key (128 bit). // Network root key (128 bit).
// Note: For LoRaWAN 1.0.x, use this field for the LoRaWAN 1.0.x 'AppKey`! // Note: For LoRaWAN 1.0.x, use this field for the LoRaWAN 1.0.x 'AppKey`!
string nwk_key = 2; string nwk_key = 2;
// Application root key (128 bit). // Application root key (128 bit).
// Note: This field only needs to be set for LoRaWAN 1.1.x devices! // Note: This field only needs to be set for LoRaWAN 1.1.x devices!
string app_key = 3; string app_key = 3;
} }
message CreateDeviceRequest { message CreateDeviceRequest {
// Device object. // Device object.
Device device = 1; Device device = 1;
} }
message GetDeviceRequest { message GetDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceResponse { message GetDeviceResponse {
// Device object. // Device object.
Device device = 1; Device device = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
// Last seen at timestamp. // Last seen at timestamp.
google.protobuf.Timestamp last_seen_at = 4; google.protobuf.Timestamp last_seen_at = 4;
// Device status. // Device status.
DeviceStatus device_status = 5; DeviceStatus device_status = 5;
// Enabled class.
DeviceClass enabled_class = 6;
} }
message UpdateDeviceRequest { message UpdateDeviceRequest {
// Device object. // Device object.
Device device = 1; Device device = 1;
} }
message DeleteDeviceRequest { message DeleteDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message ListDevicesRequest { message ListDevicesRequest {
// Max number of devices to return in the result-set. // Max number of devices to return in the result-set.
uint32 limit = 1; uint32 limit = 1;
// Offset in the result-set (for pagination). // Offset in the result-set (for pagination).
uint32 offset = 2; uint32 offset = 2;
// If set, the given string will be used to search on name (optional). // If set, the given string will be used to search on name (optional).
string search = 3; string search = 3;
// Application ID (UUID) to filter devices on. // Application ID (UUID) to filter devices on.
string application_id = 4; string application_id = 4;
// Multicst-group ID (UUID) to filter devices on. // Multicst-group ID (UUID) to filter devices on.
string multicast_group_id = 5; string multicast_group_id = 5;
} }
message ListDevicesResponse { message ListDevicesResponse {
// Total number of devices. // Total number of devices.
uint32 total_count = 1; uint32 total_count = 1;
// Result-set. // Result-set.
repeated DeviceListItem result = 2; repeated DeviceListItem result = 2;
} }
message CreateDeviceKeysRequest { message CreateDeviceKeysRequest {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
} }
message GetDeviceKeysRequest { message GetDeviceKeysRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceKeysResponse { message GetDeviceKeysResponse {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
} }
message UpdateDeviceKeysRequest { message UpdateDeviceKeysRequest {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
} }
message DeleteDeviceKeysRequest { message DeleteDeviceKeysRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message DeviceActivation { message DeviceActivation {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Device address (HEX encoded). // Device address (HEX encoded).
string dev_addr = 2; string dev_addr = 2;
// Application session key (HEX encoded). // Application session key (HEX encoded).
string app_s_key = 3; string app_s_key = 3;
// Network session encryption key (HEX encoded). // Network session encryption key (HEX encoded).
string nwk_s_enc_key = 4; string nwk_s_enc_key = 4;
// Serving network session integrity key (HEX encoded). // Serving network session integrity key (HEX encoded).
string s_nwk_s_int_key = 8; string s_nwk_s_int_key = 8;
// Forwarding network session integrity key (HEX encoded). // Forwarding network session integrity key (HEX encoded).
string f_nwk_s_int_key = 9; string f_nwk_s_int_key = 9;
// Uplink frame-counter. // Uplink frame-counter.
uint32 f_cnt_up = 5; uint32 f_cnt_up = 5;
// Downlink network frame-counter. // Downlink network frame-counter.
uint32 n_f_cnt_down = 6; uint32 n_f_cnt_down = 6;
// Downlink application frame-counter. // Downlink application frame-counter.
uint32 a_f_cnt_down = 10; uint32 a_f_cnt_down = 10;
} }
message ActivateDeviceRequest { message ActivateDeviceRequest {
// Device activation object. // Device activation object.
DeviceActivation device_activation = 1; DeviceActivation device_activation = 1;
} }
message DeactivateDeviceRequest { message DeactivateDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceActivationRequest { message GetDeviceActivationRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceActivationResponse { message GetDeviceActivationResponse {
// Device activation object. // Device activation object.
DeviceActivation device_activation = 1; DeviceActivation device_activation = 1;
} }
message GetRandomDevAddrRequest { message GetRandomDevAddrRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetRandomDevAddrResponse { message GetRandomDevAddrResponse {
// DevAddr. // DevAddr.
string dev_addr = 1; string dev_addr = 1;
} }
message GetDeviceMetricsRequest { message GetDeviceMetricsRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Interval start timestamp. // Interval start timestamp.
google.protobuf.Timestamp start = 2; google.protobuf.Timestamp start = 2;
// Interval end timestamp. // Interval end timestamp.
google.protobuf.Timestamp end = 3; google.protobuf.Timestamp end = 3;
// Aggregation. // Aggregation.
common.Aggregation aggregation = 4; common.Aggregation aggregation = 4;
} }
message GetDeviceMetricsResponse { message GetDeviceMetricsResponse {
map<string, common.Metric> metrics = 1; map<string, common.Metric> metrics = 1;
map<string, DeviceState> states = 2; map<string, DeviceState> states = 2;
} }
message DeviceState { message DeviceState {
// Name. // Name.
string name = 2; string name = 2;
// Value. // Value.
string value = 3; string value = 3;
} }
message GetDeviceLinkMetricsRequest { message GetDeviceLinkMetricsRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Interval start timestamp. // Interval start timestamp.
google.protobuf.Timestamp start = 2; google.protobuf.Timestamp start = 2;
// Interval end timestamp. // Interval end timestamp.
google.protobuf.Timestamp end = 3; google.protobuf.Timestamp end = 3;
// Aggregation. // Aggregation.
common.Aggregation aggregation = 4; common.Aggregation aggregation = 4;
} }
message GetDeviceLinkMetricsResponse { message GetDeviceLinkMetricsResponse {
// Packets received from the device. // Packets received from the device.
common.Metric rx_packets = 1; common.Metric rx_packets = 1;
// RSSI (as reported by the gateway(s)). // RSSI (as reported by the gateway(s)).
common.Metric gw_rssi = 2; common.Metric gw_rssi = 2;
// SNR (as reported by the gateway(s)). // SNR (as reported by the gateway(s)).
common.Metric gw_snr = 3; common.Metric gw_snr = 3;
// Packets received by frequency. // Packets received by frequency.
common.Metric rx_packets_per_freq = 4; common.Metric rx_packets_per_freq = 4;
// Packets received by DR. // Packets received by DR.
common.Metric rx_packets_per_dr = 5; common.Metric rx_packets_per_dr = 5;
// Errors. // Errors.
common.Metric errors = 6; common.Metric errors = 6;
} }
message DeviceQueueItem { message DeviceQueueItem {
// ID (UUID). // ID (UUID).
// This is automatically generated on enqueue. // This is automatically generated on enqueue.
string id = 1; string id = 1;
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 2; string dev_eui = 2;
// Confirmed. // Confirmed.
bool confirmed = 3; bool confirmed = 3;
// FPort (must be > 0). // FPort (must be > 0).
uint32 f_port = 4; uint32 f_port = 4;
// Data. // Data.
// Or use the json_object field when a codec has been configured. // Or use the json_object field when a codec has been configured.
bytes data = 5; bytes data = 5;
// Only use this when a codec has been configured that can encode this // Only use this when a codec has been configured that can encode this
// object to bytes. // object to bytes.
google.protobuf.Struct object = 6; google.protobuf.Struct object = 6;
// Is pending. // Is pending.
// This is set to true when the downlink is pending. // This is set to true when the downlink is pending.
bool is_pending = 7; bool is_pending = 7;
// Downlink frame-counter. // Downlink frame-counter.
// This is set when the payload has been sent as downlink. // This is set when the payload has been sent as downlink.
uint32 f_cnt_down = 8; uint32 f_cnt_down = 8;
} }
message EnqueueDeviceQueueItemRequest { message EnqueueDeviceQueueItemRequest { DeviceQueueItem queue_item = 1; }
DeviceQueueItem queue_item = 1;
}
message EnqueueDeviceQueueItemResponse { message EnqueueDeviceQueueItemResponse {
// ID (UUID). // ID (UUID).
string id = 1; string id = 1;
} }
message FlushDeviceQueueRequest { message FlushDeviceQueueRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceQueueItemsRequest { message GetDeviceQueueItemsRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Return only the count, not the result-set. // Return only the count, not the result-set.
bool count_only = 2; bool count_only = 2;
} }
message GetDeviceQueueItemsResponse { message GetDeviceQueueItemsResponse {
// Total number of queue items. // Total number of queue items.
uint32 total_count = 1; uint32 total_count = 1;
// Result-set. // Result-set.
repeated DeviceQueueItem result = 2; repeated DeviceQueueItem result = 2;
} }
message FlushDevNoncesRequest { message FlushDevNoncesRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }

View File

@ -16,520 +16,542 @@ import "google/protobuf/empty.proto";
// DeviceService is the service providing API methods for managing devices. // DeviceService is the service providing API methods for managing devices.
service DeviceService { service DeviceService {
// Create the given device. // Create the given device.
rpc Create(CreateDeviceRequest) returns (google.protobuf.Empty) { rpc Create(CreateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
post: "/api/devices" post : "/api/devices"
body: "*" body : "*"
}; };
} }
// Get returns the device for the given DevEUI. // Get returns the device for the given DevEUI.
rpc Get(GetDeviceRequest) returns (GetDeviceResponse) { rpc Get(GetDeviceRequest) returns (GetDeviceResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices/{dev_eui}" get : "/api/devices/{dev_eui}"
}; };
} }
// Update the given device. // Update the given device.
rpc Update(UpdateDeviceRequest) returns (google.protobuf.Empty) { rpc Update(UpdateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
put: "/api/devices/{device.dev_eui}" put : "/api/devices/{device.dev_eui}"
body: "*" body : "*"
}; };
} }
// Delete the device with the given DevEUI. // Delete the device with the given DevEUI.
rpc Delete(DeleteDeviceRequest) returns (google.protobuf.Empty) { rpc Delete(DeleteDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}" delete : "/api/devices/{dev_eui}"
}; };
} }
// Get the list of devices. // Get the list of devices.
rpc List(ListDevicesRequest) returns (ListDevicesResponse) { rpc List(ListDevicesRequest) returns (ListDevicesResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices" get : "/api/devices"
}; };
} }
// Create the given device-keys. // Create the given device-keys.
rpc CreateKeys(CreateDeviceKeysRequest) returns (google.protobuf.Empty) { rpc CreateKeys(CreateDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
post: "/api/devices/{device_keys.dev_eui}/keys" post : "/api/devices/{device_keys.dev_eui}/keys"
body: "*" body : "*"
}; };
} }
// Get the device-keys for the given DevEUI. // Get the device-keys for the given DevEUI.
rpc GetKeys(GetDeviceKeysRequest) returns (GetDeviceKeysResponse) { rpc GetKeys(GetDeviceKeysRequest) returns (GetDeviceKeysResponse) {
option(google.api.http) = { option (google.api.http) = {
get: "/api/devices/{dev_eui}/keys" get : "/api/devices/{dev_eui}/keys"
}; };
} }
// Update the given device-keys. // Update the given device-keys.
rpc UpdateKeys(UpdateDeviceKeysRequest) returns (google.protobuf.Empty) { rpc UpdateKeys(UpdateDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
put: "/api/devices/{device_keys.dev_eui}/keys" put : "/api/devices/{device_keys.dev_eui}/keys"
body: "*" body : "*"
}; };
} }
// Delete the device-keys for the given DevEUI. // Delete the device-keys for the given DevEUI.
rpc DeleteKeys(DeleteDeviceKeysRequest) returns (google.protobuf.Empty) { rpc DeleteKeys(DeleteDeviceKeysRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/keys" delete : "/api/devices/{dev_eui}/keys"
}; };
} }
// FlushDevNonces flushes the OTAA device nonces. // FlushDevNonces flushes the OTAA device nonces.
rpc FlushDevNonces(FlushDevNoncesRequest) returns (google.protobuf.Empty) { rpc FlushDevNonces(FlushDevNoncesRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/dev-nonces" delete : "/api/devices/{dev_eui}/dev-nonces"
}; };
} }
// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations). // Activate (re)activates the device with the given parameters (for ABP or for
rpc Activate(ActivateDeviceRequest) returns (google.protobuf.Empty) { // importing OTAA activations).
option(google.api.http) = { rpc Activate(ActivateDeviceRequest) returns (google.protobuf.Empty) {
post: "/api/devices/{device_activation.dev_eui}/activate" option (google.api.http) = {
body: "*" post : "/api/devices/{device_activation.dev_eui}/activate"
}; body : "*"
} };
}
// Deactivate de-activates the device. // Deactivate de-activates the device.
rpc Deactivate(DeactivateDeviceRequest) returns (google.protobuf.Empty) { rpc Deactivate(DeactivateDeviceRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/activation" delete : "/api/devices/{dev_eui}/activation"
}; };
} }
// GetActivation returns the current activation details of the device (OTAA or ABP). // GetActivation returns the current activation details of the device (OTAA or
rpc GetActivation(GetDeviceActivationRequest) returns (GetDeviceActivationResponse) { // ABP).
option(google.api.http) = { rpc GetActivation(GetDeviceActivationRequest)
get: "/api/devices/{dev_eui}/activation" returns (GetDeviceActivationResponse) {
}; option (google.api.http) = {
} get : "/api/devices/{dev_eui}/activation"
};
}
// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account. // GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into
rpc GetRandomDevAddr(GetRandomDevAddrRequest) returns (GetRandomDevAddrResponse) { // account.
option(google.api.http) = { rpc GetRandomDevAddr(GetRandomDevAddrRequest)
post: "/api/devices/{dev_eui}/get-random-dev-addr" returns (GetRandomDevAddrResponse) {
}; option (google.api.http) = {
} post : "/api/devices/{dev_eui}/get-random-dev-addr"
};
}
// GetMetrics returns the device metrics. // GetMetrics returns the device metrics.
// Note that this requires a device-profile with codec and measurements configured. // Note that this requires a device-profile with codec and measurements
rpc GetMetrics(GetDeviceMetricsRequest) returns (GetDeviceMetricsResponse) { // configured.
option(google.api.http) = { rpc GetMetrics(GetDeviceMetricsRequest) returns (GetDeviceMetricsResponse) {
get: "/api/devices/{dev_eui}/metrics" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/metrics"
} };
}
// GetLinkMetrics returns the device link metrics. // GetLinkMetrics returns the device link metrics.
// This includes uplinks, downlinks, RSSI, SNR, etc... // This includes uplinks, downlinks, RSSI, SNR, etc...
rpc GetLinkMetrics(GetDeviceLinkMetricsRequest) returns (GetDeviceLinkMetricsResponse) { rpc GetLinkMetrics(GetDeviceLinkMetricsRequest)
option(google.api.http) = { returns (GetDeviceLinkMetricsResponse) {
get: "/api/devices/{dev_eui}/link-metrics" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/link-metrics"
} };
}
// Enqueue adds the given item to the downlink queue. // Enqueue adds the given item to the downlink queue.
rpc Enqueue(EnqueueDeviceQueueItemRequest) returns (EnqueueDeviceQueueItemResponse) { rpc Enqueue(EnqueueDeviceQueueItemRequest)
option(google.api.http) = { returns (EnqueueDeviceQueueItemResponse) {
post: "/api/devices/{queue_item.dev_eui}/queue" option (google.api.http) = {
body: "*" post : "/api/devices/{queue_item.dev_eui}/queue"
}; body : "*"
} };
}
// FlushQueue flushes the downlink device-queue. // FlushQueue flushes the downlink device-queue.
rpc FlushQueue(FlushDeviceQueueRequest) returns (google.protobuf.Empty) { rpc FlushQueue(FlushDeviceQueueRequest) returns (google.protobuf.Empty) {
option(google.api.http) = { option (google.api.http) = {
delete: "/api/devices/{dev_eui}/queue" delete : "/api/devices/{dev_eui}/queue"
}; };
} }
// GetQueue returns the downlink device-queue. // GetQueue returns the downlink device-queue.
rpc GetQueue(GetDeviceQueueItemsRequest) returns (GetDeviceQueueItemsResponse) { rpc GetQueue(GetDeviceQueueItemsRequest)
option(google.api.http) = { returns (GetDeviceQueueItemsResponse) {
get: "/api/devices/{dev_eui}/queue" option (google.api.http) = {
}; get : "/api/devices/{dev_eui}/queue"
} };
}
}
enum DeviceClass {
// Class-A.
A = 0;
// Class-B.
B = 1;
// Class-C.
C = 2;
} }
message Device { message Device {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Name. // Name.
string name = 2; string name = 2;
// Description. // Description.
string description = 3; string description = 3;
// Application ID (UUID). // Application ID (UUID).
string application_id = 4; string application_id = 4;
// Device-profile ID (UUID). // Device-profile ID (UUID).
string device_profile_id = 5; string device_profile_id = 5;
// Skip frame-counter checks (this is insecure, but could be helpful for debugging). // Skip frame-counter checks (this is insecure, but could be helpful for
bool skip_fcnt_check = 6; // debugging).
bool skip_fcnt_check = 6;
// Device is disabled. // Device is disabled.
bool is_disabled = 7; bool is_disabled = 7;
// Variables (user defined). // Variables (user defined).
// These variables can be used together with integrations to store tokens / // These variables can be used together with integrations to store tokens /
// secrets that must be configured per device. These variables are not // secrets that must be configured per device. These variables are not
// exposed in the event payloads. // exposed in the event payloads.
map<string, string> variables = 8; map<string, string> variables = 8;
// Tags (user defined). // Tags (user defined).
// These tags are exposed in the event payloads or to integration. Tags are // These tags are exposed in the event payloads or to integration. Tags are
// intended for aggregation and filtering. // intended for aggregation and filtering.
map<string, string> tags = 9; map<string, string> tags = 9;
// JoinEUI (optional, EUI64). // JoinEUI (optional, EUI64).
// This field will be automatically set / updated on OTAA. However, in some // This field will be automatically set / updated on OTAA. However, in some
// cases it must be pre-configured. For example to allow OTAA using a Relay. // cases it must be pre-configured. For example to allow OTAA using a Relay.
// In this case the Relay needs to know the JoinEUI + DevEUI combinations // In this case the Relay needs to know the JoinEUI + DevEUI combinations
// of the devices for which it needs to forward uplinks. // of the devices for which it needs to forward uplinks.
string join_eui = 10; string join_eui = 10;
} }
message DeviceStatus { message DeviceStatus {
// The device margin status // The device margin status
// -32..32: The demodulation SNR ration in dB // -32..32: The demodulation SNR ration in dB
int32 margin = 1; int32 margin = 1;
// Device is connected to an external power source. // Device is connected to an external power source.
bool external_power_source = 2; bool external_power_source = 2;
// Device battery level as a percentage. // Device battery level as a percentage.
// -1 when the battery level is not available. // -1 when the battery level is not available.
float battery_level = 3; float battery_level = 3;
} }
message DeviceListItem { message DeviceListItem {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
// Last seen at timestamp. // Last seen at timestamp.
google.protobuf.Timestamp last_seen_at = 4; google.protobuf.Timestamp last_seen_at = 4;
// Name. // Name.
string name = 5; string name = 5;
// Description. // Description.
string description = 6; string description = 6;
// Device-profile ID (UUID). // Device-profile ID (UUID).
string device_profile_id = 7; string device_profile_id = 7;
// Device-profile name. // Device-profile name.
string device_profile_name = 8; string device_profile_name = 8;
// Device status. // Device status.
DeviceStatus device_status = 9; DeviceStatus device_status = 9;
} }
message DeviceKeys { message DeviceKeys {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Network root key (128 bit). // Network root key (128 bit).
// Note: For LoRaWAN 1.0.x, use this field for the LoRaWAN 1.0.x 'AppKey`! // Note: For LoRaWAN 1.0.x, use this field for the LoRaWAN 1.0.x 'AppKey`!
string nwk_key = 2; string nwk_key = 2;
// Application root key (128 bit). // Application root key (128 bit).
// Note: This field only needs to be set for LoRaWAN 1.1.x devices! // Note: This field only needs to be set for LoRaWAN 1.1.x devices!
string app_key = 3; string app_key = 3;
} }
message CreateDeviceRequest { message CreateDeviceRequest {
// Device object. // Device object.
Device device = 1; Device device = 1;
} }
message GetDeviceRequest { message GetDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceResponse { message GetDeviceResponse {
// Device object. // Device object.
Device device = 1; Device device = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
// Last seen at timestamp. // Last seen at timestamp.
google.protobuf.Timestamp last_seen_at = 4; google.protobuf.Timestamp last_seen_at = 4;
// Device status. // Device status.
DeviceStatus device_status = 5; DeviceStatus device_status = 5;
// Enabled class.
DeviceClass enabled_class = 6;
} }
message UpdateDeviceRequest { message UpdateDeviceRequest {
// Device object. // Device object.
Device device = 1; Device device = 1;
} }
message DeleteDeviceRequest { message DeleteDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message ListDevicesRequest { message ListDevicesRequest {
// Max number of devices to return in the result-set. // Max number of devices to return in the result-set.
uint32 limit = 1; uint32 limit = 1;
// Offset in the result-set (for pagination). // Offset in the result-set (for pagination).
uint32 offset = 2; uint32 offset = 2;
// If set, the given string will be used to search on name (optional). // If set, the given string will be used to search on name (optional).
string search = 3; string search = 3;
// Application ID (UUID) to filter devices on. // Application ID (UUID) to filter devices on.
string application_id = 4; string application_id = 4;
// Multicst-group ID (UUID) to filter devices on. // Multicst-group ID (UUID) to filter devices on.
string multicast_group_id = 5; string multicast_group_id = 5;
} }
message ListDevicesResponse { message ListDevicesResponse {
// Total number of devices. // Total number of devices.
uint32 total_count = 1; uint32 total_count = 1;
// Result-set. // Result-set.
repeated DeviceListItem result = 2; repeated DeviceListItem result = 2;
} }
message CreateDeviceKeysRequest { message CreateDeviceKeysRequest {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
} }
message GetDeviceKeysRequest { message GetDeviceKeysRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceKeysResponse { message GetDeviceKeysResponse {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
// Created at timestamp. // Created at timestamp.
google.protobuf.Timestamp created_at = 2; google.protobuf.Timestamp created_at = 2;
// Last update timestamp. // Last update timestamp.
google.protobuf.Timestamp updated_at = 3; google.protobuf.Timestamp updated_at = 3;
} }
message UpdateDeviceKeysRequest { message UpdateDeviceKeysRequest {
// Device-keys object. // Device-keys object.
DeviceKeys device_keys = 1; DeviceKeys device_keys = 1;
} }
message DeleteDeviceKeysRequest { message DeleteDeviceKeysRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message DeviceActivation { message DeviceActivation {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Device address (HEX encoded). // Device address (HEX encoded).
string dev_addr = 2; string dev_addr = 2;
// Application session key (HEX encoded). // Application session key (HEX encoded).
string app_s_key = 3; string app_s_key = 3;
// Network session encryption key (HEX encoded). // Network session encryption key (HEX encoded).
string nwk_s_enc_key = 4; string nwk_s_enc_key = 4;
// Serving network session integrity key (HEX encoded). // Serving network session integrity key (HEX encoded).
string s_nwk_s_int_key = 8; string s_nwk_s_int_key = 8;
// Forwarding network session integrity key (HEX encoded). // Forwarding network session integrity key (HEX encoded).
string f_nwk_s_int_key = 9; string f_nwk_s_int_key = 9;
// Uplink frame-counter. // Uplink frame-counter.
uint32 f_cnt_up = 5; uint32 f_cnt_up = 5;
// Downlink network frame-counter. // Downlink network frame-counter.
uint32 n_f_cnt_down = 6; uint32 n_f_cnt_down = 6;
// Downlink application frame-counter. // Downlink application frame-counter.
uint32 a_f_cnt_down = 10; uint32 a_f_cnt_down = 10;
} }
message ActivateDeviceRequest { message ActivateDeviceRequest {
// Device activation object. // Device activation object.
DeviceActivation device_activation = 1; DeviceActivation device_activation = 1;
} }
message DeactivateDeviceRequest { message DeactivateDeviceRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceActivationRequest { message GetDeviceActivationRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceActivationResponse { message GetDeviceActivationResponse {
// Device activation object. // Device activation object.
DeviceActivation device_activation = 1; DeviceActivation device_activation = 1;
} }
message GetRandomDevAddrRequest { message GetRandomDevAddrRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetRandomDevAddrResponse { message GetRandomDevAddrResponse {
// DevAddr. // DevAddr.
string dev_addr = 1; string dev_addr = 1;
} }
message GetDeviceMetricsRequest { message GetDeviceMetricsRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Interval start timestamp. // Interval start timestamp.
google.protobuf.Timestamp start = 2; google.protobuf.Timestamp start = 2;
// Interval end timestamp. // Interval end timestamp.
google.protobuf.Timestamp end = 3; google.protobuf.Timestamp end = 3;
// Aggregation. // Aggregation.
common.Aggregation aggregation = 4; common.Aggregation aggregation = 4;
} }
message GetDeviceMetricsResponse { message GetDeviceMetricsResponse {
map<string, common.Metric> metrics = 1; map<string, common.Metric> metrics = 1;
map<string, DeviceState> states = 2; map<string, DeviceState> states = 2;
} }
message DeviceState { message DeviceState {
// Name. // Name.
string name = 2; string name = 2;
// Value. // Value.
string value = 3; string value = 3;
} }
message GetDeviceLinkMetricsRequest { message GetDeviceLinkMetricsRequest {
// DevEUI (EUI64). // DevEUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Interval start timestamp. // Interval start timestamp.
google.protobuf.Timestamp start = 2; google.protobuf.Timestamp start = 2;
// Interval end timestamp. // Interval end timestamp.
google.protobuf.Timestamp end = 3; google.protobuf.Timestamp end = 3;
// Aggregation. // Aggregation.
common.Aggregation aggregation = 4; common.Aggregation aggregation = 4;
} }
message GetDeviceLinkMetricsResponse { message GetDeviceLinkMetricsResponse {
// Packets received from the device. // Packets received from the device.
common.Metric rx_packets = 1; common.Metric rx_packets = 1;
// RSSI (as reported by the gateway(s)). // RSSI (as reported by the gateway(s)).
common.Metric gw_rssi = 2; common.Metric gw_rssi = 2;
// SNR (as reported by the gateway(s)). // SNR (as reported by the gateway(s)).
common.Metric gw_snr = 3; common.Metric gw_snr = 3;
// Packets received by frequency. // Packets received by frequency.
common.Metric rx_packets_per_freq = 4; common.Metric rx_packets_per_freq = 4;
// Packets received by DR. // Packets received by DR.
common.Metric rx_packets_per_dr = 5; common.Metric rx_packets_per_dr = 5;
// Errors. // Errors.
common.Metric errors = 6; common.Metric errors = 6;
} }
message DeviceQueueItem { message DeviceQueueItem {
// ID (UUID). // ID (UUID).
// This is automatically generated on enqueue. // This is automatically generated on enqueue.
string id = 1; string id = 1;
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 2; string dev_eui = 2;
// Confirmed. // Confirmed.
bool confirmed = 3; bool confirmed = 3;
// FPort (must be > 0). // FPort (must be > 0).
uint32 f_port = 4; uint32 f_port = 4;
// Data. // Data.
// Or use the json_object field when a codec has been configured. // Or use the json_object field when a codec has been configured.
bytes data = 5; bytes data = 5;
// Only use this when a codec has been configured that can encode this // Only use this when a codec has been configured that can encode this
// object to bytes. // object to bytes.
google.protobuf.Struct object = 6; google.protobuf.Struct object = 6;
// Is pending. // Is pending.
// This is set to true when the downlink is pending. // This is set to true when the downlink is pending.
bool is_pending = 7; bool is_pending = 7;
// Downlink frame-counter. // Downlink frame-counter.
// This is set when the payload has been sent as downlink. // This is set when the payload has been sent as downlink.
uint32 f_cnt_down = 8; uint32 f_cnt_down = 8;
} }
message EnqueueDeviceQueueItemRequest { message EnqueueDeviceQueueItemRequest { DeviceQueueItem queue_item = 1; }
DeviceQueueItem queue_item = 1;
}
message EnqueueDeviceQueueItemResponse { message EnqueueDeviceQueueItemResponse {
// ID (UUID). // ID (UUID).
string id = 1; string id = 1;
} }
message FlushDeviceQueueRequest { message FlushDeviceQueueRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }
message GetDeviceQueueItemsRequest { message GetDeviceQueueItemsRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
// Return only the count, not the result-set. // Return only the count, not the result-set.
bool count_only = 2; bool count_only = 2;
} }
message GetDeviceQueueItemsResponse { message GetDeviceQueueItemsResponse {
// Total number of queue items. // Total number of queue items.
uint32 total_count = 1; uint32 total_count = 1;
// Result-set. // Result-set.
repeated DeviceQueueItem result = 2; repeated DeviceQueueItem result = 2;
} }
message FlushDevNoncesRequest { message FlushDevNoncesRequest {
// Device EUI (EUI64). // Device EUI (EUI64).
string dev_eui = 1; string dev_eui = 1;
} }

View File

@ -132,6 +132,7 @@ impl DeviceService for Device {
}), }),
false => None, false => None,
}, },
enabled_class: d.enabled_class.to_proto().into(),
}); });
resp.metadata_mut() resp.metadata_mut()
.insert("x-log-dev_eui", req.dev_eui.parse().unwrap()); .insert("x-log-dev_eui", req.dev_eui.parse().unwrap());

View File

@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
use crate::codec::Codec; use crate::codec::Codec;
use crate::storage::fields::{MeasurementKind, MulticastGroupSchedulingType}; use crate::storage::fields::{MeasurementKind, MulticastGroupSchedulingType};
use crate::storage::metrics::Aggregation; use crate::storage::{device::DeviceClass, metrics::Aggregation};
use chirpstack_api::{api, common}; use chirpstack_api::{api, common};
use lrwn::region::{CommonName, MacVersion, Revision}; use lrwn::region::{CommonName, MacVersion, Revision};
@ -249,6 +249,16 @@ impl FromProto<lrwn::RelayModeActivation> for api::RelayModeActivation {
} }
} }
impl ToProto<api::DeviceClass> for DeviceClass {
fn to_proto(self) -> api::DeviceClass {
match self {
DeviceClass::A => api::DeviceClass::A,
DeviceClass::B => api::DeviceClass::B,
DeviceClass::C => api::DeviceClass::C,
}
}
}
pub fn datetime_to_prost_timestamp(dt: &DateTime<Utc>) -> prost_types::Timestamp { pub fn datetime_to_prost_timestamp(dt: &DateTime<Utc>) -> prost_types::Timestamp {
let ts = dt.timestamp_nanos(); let ts = dt.timestamp_nanos();

View File

@ -14,8 +14,10 @@ use crate::downlink::{classb, helpers, tx_ack};
use crate::gpstime::{ToDateTime, ToGpsTime}; use crate::gpstime::{ToDateTime, ToGpsTime};
use crate::storage; use crate::storage;
use crate::storage::{ use crate::storage::{
application, device, device_gateway, device_profile, device_queue, device_session, application,
downlink_frame, mac_command, relay, tenant, device::{self, DeviceClass},
device_gateway, device_profile, device_queue, device_session, downlink_frame, mac_command,
relay, tenant,
}; };
use crate::uplink::{RelayContext, UplinkFrameSet}; use crate::uplink::{RelayContext, UplinkFrameSet};
use crate::{adr, config, gateway, integration, maccommand, region, sensitivity}; use crate::{adr, config, gateway, integration, maccommand, region, sensitivity};
@ -548,15 +550,15 @@ impl Data {
} }
fn _is_class_a(&self) -> bool { fn _is_class_a(&self) -> bool {
&self.device.enabled_class == "A" self.device.enabled_class == DeviceClass::A
} }
fn _is_class_b(&self) -> bool { fn _is_class_b(&self) -> bool {
&self.device.enabled_class == "B" self.device.enabled_class == DeviceClass::B
} }
fn _is_class_c(&self) -> bool { fn _is_class_c(&self) -> bool {
&self.device.enabled_class == "C" self.device.enabled_class == DeviceClass::C
} }
fn _is_roaming(&self) -> bool { fn _is_roaming(&self) -> bool {

View File

@ -8,8 +8,9 @@ use lrwn::{AES128Key, MType, Payload, PhyPayload, EUI64};
use crate::api::helpers::ToProto; use crate::api::helpers::ToProto;
use crate::storage::{ use crate::storage::{
application, device, device_profile, device_queue, device_session, downlink_frame, multicast, application,
tenant, device::{self, DeviceClass},
device_profile, device_queue, device_session, downlink_frame, multicast, tenant,
}; };
use crate::{framelog, integration, metalog}; use crate::{framelog, integration, metalog};
use chirpstack_api::{api, common, gw, integration as integration_pb, internal, meta}; use chirpstack_api::{api, common, gw, integration as integration_pb, internal, meta};
@ -340,7 +341,7 @@ impl TxAck {
qi.is_pending = true; qi.is_pending = true;
if &dev.enabled_class == "C" { if dev.enabled_class == DeviceClass::C {
let timeout = Utc::now() + Duration::seconds(dp.class_c_timeout as i64); let timeout = Utc::now() + Duration::seconds(dp.class_c_timeout as i64);
qi.timeout_after = Some(timeout); qi.timeout_after = Some(timeout);
} }

View File

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use crate::storage::device; use crate::storage::device::{self, DeviceClass};
pub async fn handle( pub async fn handle(
dev: &device::Device, dev: &device::Device,
@ -10,7 +10,14 @@ pub async fn handle(
.first() .first()
.ok_or_else(|| anyhow!("Expected DeviceModeInd"))?; .ok_or_else(|| anyhow!("Expected DeviceModeInd"))?;
if let lrwn::MACCommand::DeviceModeInd(pl) = mac { if let lrwn::MACCommand::DeviceModeInd(pl) = mac {
device::set_enabled_class(&dev.dev_eui, &pl.class.to_string()).await?; device::set_enabled_class(
&dev.dev_eui,
match pl.class {
lrwn::DeviceModeClass::ClassA => DeviceClass::A,
lrwn::DeviceModeClass::ClassC => DeviceClass::C,
},
)
.await?;
return Ok(Some(lrwn::MACCommandSet::new(vec![ return Ok(Some(lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::DeviceModeConf(lrwn::DeviceModeConfPayload { class: pl.class }), lrwn::MACCommand::DeviceModeConf(lrwn::DeviceModeConfPayload { class: pl.class }),
@ -81,6 +88,6 @@ pub mod test {
); );
let d = device::get(&dev.dev_eui).await.unwrap(); let d = device::get(&dev.dev_eui).await.unwrap();
assert_eq!("C".to_string(), d.enabled_class); assert_eq!(DeviceClass::C, d.enabled_class);
} }
} }

View File

@ -1,10 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use bigdecimal::BigDecimal; use bigdecimal::BigDecimal;
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Duration, Utc};
use diesel::dsl; use diesel::{backend::Backend, deserialize, dsl, prelude::*, serialize, sql_types::Text};
use diesel::prelude::*;
use tokio::task; use tokio::task;
use tracing::info; use tracing::info;
use uuid::Uuid; use uuid::Uuid;
@ -15,6 +16,59 @@ use super::schema::{application, device, device_profile, multicast_group_device,
use super::{error::Error, fields, get_db_conn, get_redis_conn, redis_key}; use super::{error::Error, fields, get_db_conn, get_redis_conn, redis_key};
use crate::config; use crate::config;
#[derive(Debug, Clone, Copy, Eq, PartialEq, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
pub enum DeviceClass {
A,
B,
C,
}
impl fmt::Display for DeviceClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl FromStr for DeviceClass {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"A" => DeviceClass::A,
"B" => DeviceClass::B,
"C" => DeviceClass::C,
_ => return Err(anyhow!("Unexpected DeviceClass: {}", s)),
})
}
}
impl<DB> deserialize::FromSql<Text, DB> for DeviceClass
where
DB: Backend,
*const str: deserialize::FromSql<Text, DB>,
{
fn from_sql(value: <DB as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
let string = String::from_sql(value)?;
Ok(DeviceClass::from_str(&string)?)
}
}
impl serialize::ToSql<Text, diesel::pg::Pg> for DeviceClass
where
str: serialize::ToSql<Text, diesel::pg::Pg>,
{
fn to_sql<'b>(
&'b self,
out: &mut serialize::Output<'b, '_, diesel::pg::Pg>,
) -> serialize::Result {
<str as serialize::ToSql<Text, diesel::pg::Pg>>::to_sql(
&self.to_string(),
&mut out.reborrow(),
)
}
}
#[derive(Queryable, QueryableByName, Insertable, PartialEq, Debug, Clone)] #[derive(Queryable, QueryableByName, Insertable, PartialEq, Debug, Clone)]
#[diesel(table_name = device)] #[diesel(table_name = device)]
pub struct Device { pub struct Device {
@ -35,7 +89,7 @@ pub struct Device {
pub longitude: Option<f64>, pub longitude: Option<f64>,
pub altitude: Option<f32>, pub altitude: Option<f32>,
pub dev_addr: Option<DevAddr>, pub dev_addr: Option<DevAddr>,
pub enabled_class: String, pub enabled_class: DeviceClass,
pub skip_fcnt_check: bool, pub skip_fcnt_check: bool,
pub is_disabled: bool, pub is_disabled: bool,
pub tags: fields::KeyValue, pub tags: fields::KeyValue,
@ -74,7 +128,7 @@ impl Default for Device {
longitude: None, longitude: None,
altitude: None, altitude: None,
dev_addr: None, dev_addr: None,
enabled_class: "A".into(), enabled_class: DeviceClass::A,
skip_fcnt_check: false, skip_fcnt_check: false,
is_disabled: false, is_disabled: false,
tags: fields::KeyValue::new(HashMap::new()), tags: fields::KeyValue::new(HashMap::new()),
@ -212,10 +266,10 @@ pub async fn update(d: Device) -> Result<Device, Error> {
Ok(d) Ok(d)
} }
pub async fn set_enabled_class(dev_eui: &EUI64, mode: &str) -> Result<Device, Error> { pub async fn set_enabled_class(dev_eui: &EUI64, mode: DeviceClass) -> Result<Device, Error> {
let d = task::spawn_blocking({ let d = task::spawn_blocking({
let dev_eui = *dev_eui; let dev_eui = *dev_eui;
let mode = mode.to_string();
move || -> Result<Device, Error> { move || -> Result<Device, Error> {
let mut c = get_db_conn()?; let mut c = get_db_conn()?;
diesel::update(device::dsl::device.find(&dev_eui)) diesel::update(device::dsl::device.find(&dev_eui))
@ -746,13 +800,13 @@ pub mod test {
assert_eq!(0, res.len()); assert_eq!(0, res.len());
// device in Class-B. // device in Class-B.
let d = set_enabled_class(&d.dev_eui, "B").await.unwrap(); let d = set_enabled_class(&d.dev_eui, DeviceClass::B).await.unwrap();
let res = get_with_class_b_c_queue_items(10).await.unwrap(); let res = get_with_class_b_c_queue_items(10).await.unwrap();
let d = set_scheduler_run_after(&d.dev_eui, None).await.unwrap(); let d = set_scheduler_run_after(&d.dev_eui, None).await.unwrap();
assert_eq!(1, res.len()); assert_eq!(1, res.len());
// device in Class-C // device in Class-C
let d = set_enabled_class(&d.dev_eui, "C").await.unwrap(); let d = set_enabled_class(&d.dev_eui, DeviceClass::C).await.unwrap();
let res = get_with_class_b_c_queue_items(10).await.unwrap(); let res = get_with_class_b_c_queue_items(10).await.unwrap();
assert_eq!(1, res.len()); assert_eq!(1, res.len());

View File

@ -11,7 +11,8 @@ use tokio::time::sleep;
use crate::gateway::backend::mock as gateway_mock; use crate::gateway::backend::mock as gateway_mock;
use crate::integration::mock; use crate::integration::mock;
use crate::storage::{ use crate::storage::{
device, device_queue, device_session, downlink_frame, get_redis_conn, redis_key, device::{self, DeviceClass},
device_queue, device_session, downlink_frame, get_redis_conn, redis_key,
}; };
use chirpstack_api::{api, gw, integration as integration_pb, internal, meta}; use chirpstack_api::{api, gw, integration as integration_pb, internal, meta};
use lrwn::EUI64; use lrwn::EUI64;
@ -362,7 +363,7 @@ pub fn device_queue_items(dev_eui: EUI64, items: Vec<device_queue::DeviceQueueIt
}) })
} }
pub fn enabled_class(dev_eui: EUI64, c: String) -> Validator { pub fn enabled_class(dev_eui: EUI64, c: DeviceClass) -> Validator {
Box::new(move || { Box::new(move || {
let c = c.clone(); let c = c.clone();
let dev_eui = dev_eui.clone(); let dev_eui = dev_eui.clone();

View File

@ -10,7 +10,9 @@ use crate::api::backend as backend_api;
use crate::backend::{joinserver, roaming}; use crate::backend::{joinserver, roaming};
use crate::gateway::backend as gateway_backend; use crate::gateway::backend as gateway_backend;
use crate::storage::{ use crate::storage::{
application, device, device_profile, device_queue, device_session, gateway, tenant, application,
device::{self, DeviceClass},
device_profile, device_queue, device_session, gateway, tenant,
}; };
use crate::{config, test, uplink}; use crate::{config, test, uplink};
use chirpstack_api::{common, gw, internal}; use chirpstack_api::{common, gw, internal};
@ -222,7 +224,7 @@ async fn test_sns_uplink() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -6,8 +6,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::storage::{ use crate::storage::{
application, device, device_profile, device_queue, device_session, gateway, mac_command, application,
reset_redis, tenant, device::{self, DeviceClass},
device_profile, device_queue, device_session, gateway, mac_command, reset_redis, tenant,
}; };
use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink}; use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
use chirpstack_api::{api, common, gw, integration as integration_pb, internal}; use chirpstack_api::{api, common, gw, integration as integration_pb, internal};
@ -91,7 +92,7 @@ async fn test_gateway_filtering() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await
@ -255,7 +256,7 @@ async fn test_region_config_id_filtering() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -414,7 +415,7 @@ async fn test_lorawan_10_errors() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -609,7 +610,7 @@ async fn test_lorawan_11_errors() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -762,7 +763,7 @@ async fn test_lorawan_10_skip_f_cnt() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
skip_fcnt_check: true, skip_fcnt_check: true,
..Default::default() ..Default::default()
}) })
@ -955,7 +956,7 @@ async fn test_lorawan_10_device_disabled() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
is_disabled: true, is_disabled: true,
..Default::default() ..Default::default()
}) })
@ -1079,7 +1080,7 @@ async fn test_lorawan_10_uplink() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -1698,7 +1699,7 @@ async fn test_lorawan_11_uplink() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -1936,7 +1937,7 @@ async fn test_lorawan_10_rx_delay() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -2149,7 +2150,7 @@ async fn test_lorawan_10_mac_commands() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -2518,7 +2519,7 @@ async fn test_lorawan_11_mac_commands() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -2712,7 +2713,7 @@ async fn test_lorawan_10_device_queue() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -3185,7 +3186,7 @@ async fn test_lorawan_11_device_queue() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -3662,7 +3663,7 @@ async fn test_lorawan_10_adr() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -4505,7 +4506,7 @@ async fn test_lorawan_10_device_status_request() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -4768,7 +4769,7 @@ async fn test_lorawan_11_receive_window_selection() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -3,8 +3,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::gpstime::ToGpsTime; use crate::gpstime::ToGpsTime;
use crate::storage::{ use crate::storage::{
application, device, device_gateway, device_profile, device_queue, device_session, gateway, application,
reset_redis, tenant, device::{self, DeviceClass},
device_gateway, device_profile, device_queue, device_session, gateway, reset_redis, tenant,
}; };
use crate::{ use crate::{
config, downlink, downlink::classb, gateway::backend as gateway_backend, integration, test, config, downlink, downlink::classb, gateway::backend as gateway_backend, integration, test,
@ -78,7 +79,7 @@ async fn test_uplink() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -155,7 +156,7 @@ async fn test_uplink() {
}, },
assert: vec![ assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 9), assert::f_cnt_up(dev.dev_eui.clone(), 9),
assert::enabled_class(dev.dev_eui.clone(), "B".into()), assert::enabled_class(dev.dev_eui.clone(), DeviceClass::B),
], ],
}) })
.await; .await;
@ -189,7 +190,7 @@ async fn test_uplink() {
}, },
assert: vec![ assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 9), assert::f_cnt_up(dev.dev_eui.clone(), 9),
assert::enabled_class(dev.dev_eui.clone(), "A".into()), assert::enabled_class(dev.dev_eui.clone(), DeviceClass::A),
], ],
}) })
.await; .await;
@ -242,7 +243,7 @@ async fn test_downlink_scheduler() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -2,8 +2,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::storage::{ use crate::storage::{
application, device, device_gateway, device_profile, device_queue, device_session, gateway, application,
reset_redis, tenant, device::{self, DeviceClass},
device_gateway, device_profile, device_queue, device_session, gateway, reset_redis, tenant,
}; };
use crate::{downlink, gateway::backend as gateway_backend, integration, test}; use crate::{downlink, gateway::backend as gateway_backend, integration, test};
use chirpstack_api::{common, gw, internal}; use chirpstack_api::{common, gw, internal};
@ -69,7 +70,7 @@ async fn test_downlink_scheduler() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "C".into(), enabled_class: DeviceClass::C,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -10,7 +10,11 @@ use super::assert;
use crate::api::backend as backend_api; use crate::api::backend as backend_api;
use crate::backend::{joinserver, roaming}; use crate::backend::{joinserver, roaming};
use crate::gateway::backend as gateway_backend; use crate::gateway::backend as gateway_backend;
use crate::storage::{application, device, device_keys, device_profile, gateway, tenant}; use crate::storage::{
application,
device::{self, DeviceClass},
device_keys, device_profile, gateway, tenant,
};
use crate::{config, test, uplink}; use crate::{config, test, uplink};
use chirpstack_api::gw; use chirpstack_api::gw;
use lrwn::{AES128Key, NetID, EUI64}; use lrwn::{AES128Key, NetID, EUI64};
@ -298,7 +302,7 @@ async fn test_sns() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -6,7 +6,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::storage::{ use crate::storage::{
application, device, device_keys, device_profile, gateway, reset_redis, tenant, application,
device::{self, DeviceClass},
device_keys, device_profile, gateway, reset_redis, tenant,
}; };
use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink}; use crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
use chirpstack_api::{common, gw, internal, meta}; use chirpstack_api::{common, gw, internal, meta};
@ -89,7 +91,7 @@ async fn test_gateway_filtering() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await
@ -261,7 +263,7 @@ async fn test_lorawan_10() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await
@ -560,7 +562,7 @@ async fn test_lorawan_10() {
}), }),
..Default::default() ..Default::default()
}), }),
assert::enabled_class(dev.dev_eui.clone(), "A".into()), assert::enabled_class(dev.dev_eui.clone(), DeviceClass::A),
assert::device_queue_items(dev.dev_eui.clone(), vec![]), assert::device_queue_items(dev.dev_eui.clone(), vec![]),
assert::uplink_meta_log(meta::UplinkMeta { assert::uplink_meta_log(meta::UplinkMeta {
dev_eui: dev.dev_eui.to_string(), dev_eui: dev.dev_eui.to_string(),
@ -807,7 +809,7 @@ async fn test_lorawan_10() {
tx_info: tx_info.clone(), tx_info: tx_info.clone(),
phy_payload: jr_pl.clone(), phy_payload: jr_pl.clone(),
extra_uplink_channels: Vec::new(), extra_uplink_channels: Vec::new(),
assert: vec![assert::enabled_class(dev.dev_eui.clone(), "A".to_string())], assert: vec![assert::enabled_class(dev.dev_eui.clone(), DeviceClass::A)],
}, },
Test { Test {
name: "join-request accepted + class-c supported".into(), name: "join-request accepted + class-c supported".into(),
@ -834,7 +836,7 @@ async fn test_lorawan_10() {
tx_info: tx_info.clone(), tx_info: tx_info.clone(),
phy_payload: jr_pl.clone(), phy_payload: jr_pl.clone(),
extra_uplink_channels: Vec::new(), extra_uplink_channels: Vec::new(),
assert: vec![assert::enabled_class(dev.dev_eui.clone(), "C".to_string())], assert: vec![assert::enabled_class(dev.dev_eui.clone(), DeviceClass::C)],
}, },
Test { Test {
name: "device disabled".into(), name: "device disabled".into(),
@ -915,7 +917,7 @@ async fn test_lorawan_11() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
enabled_class: "B".into(), enabled_class: DeviceClass::B,
..Default::default() ..Default::default()
}) })
.await .await
@ -1181,7 +1183,7 @@ async fn test_lorawan_11() {
}), }),
..Default::default() ..Default::default()
}), }),
assert::enabled_class(dev.dev_eui.clone(), "A".into()), assert::enabled_class(dev.dev_eui.clone(), DeviceClass::A),
assert::device_queue_items(dev.dev_eui.clone(), vec![]), assert::device_queue_items(dev.dev_eui.clone(), vec![]),
], ],
}, },
@ -1210,7 +1212,7 @@ async fn test_lorawan_11() {
tx_info: tx_info.clone(), tx_info: tx_info.clone(),
phy_payload: jr_pl.clone(), phy_payload: jr_pl.clone(),
extra_uplink_channels: Vec::new(), extra_uplink_channels: Vec::new(),
assert: vec![assert::enabled_class(dev.dev_eui.clone(), "A".to_string())], assert: vec![assert::enabled_class(dev.dev_eui.clone(), DeviceClass::A)],
}, },
]; ];

View File

@ -4,7 +4,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::storage::{ use crate::storage::{
application, device, device_profile, device_queue, device_session, gateway, reset_redis, tenant, application,
device::{self, DeviceClass},
device_profile, device_queue, device_session, gateway, reset_redis, tenant,
}; };
use crate::{gateway::backend as gateway_backend, integration, test, uplink}; use crate::{gateway::backend as gateway_backend, integration, test, uplink};
use chirpstack_api::{common, gw, integration as integration_pb, internal}; use chirpstack_api::{common, gw, integration as integration_pb, internal};
@ -82,7 +84,7 @@ async fn test_lorawan_10() {
application_id: app.id, application_id: app.id,
device_profile_id: dp_relay.id, device_profile_id: dp_relay.id,
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]), dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -93,7 +95,7 @@ async fn test_lorawan_10() {
application_id: app.id, application_id: app.id,
device_profile_id: dp_relay_ed.id, device_profile_id: dp_relay_ed.id,
dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]), dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -4,7 +4,9 @@ use uuid::Uuid;
use super::assert; use super::assert;
use crate::storage::{ use crate::storage::{
application, device, device_keys, device_profile, device_session, gateway, tenant, application,
device::{self, DeviceClass},
device_keys, device_profile, device_session, gateway, tenant,
}; };
use crate::{gateway::backend as gateway_backend, integration, test, uplink}; use crate::{gateway::backend as gateway_backend, integration, test, uplink};
use chirpstack_api::{common, gw, internal}; use chirpstack_api::{common, gw, internal};
@ -82,7 +84,7 @@ async fn test_lorawan_10() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp.id.clone(), device_profile_id: dp.id.clone(),
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]), dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await
@ -101,7 +103,7 @@ async fn test_lorawan_10() {
application_id: app.id.clone(), application_id: app.id.clone(),
device_profile_id: dp_relay.id.clone(), device_profile_id: dp_relay.id.clone(),
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 2]), dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 2]),
enabled_class: "A".into(), enabled_class: DeviceClass::A,
..Default::default() ..Default::default()
}) })
.await .await

View File

@ -14,8 +14,9 @@ use crate::api::helpers::ToProto;
use crate::backend::roaming; use crate::backend::roaming;
use crate::storage::error::Error as StorageError; use crate::storage::error::Error as StorageError;
use crate::storage::{ use crate::storage::{
application, device, device_gateway, device_profile, device_queue, device_session, fields, application,
metrics, tenant, device::{self, DeviceClass},
device_gateway, device_profile, device_queue, device_session, fields, metrics, tenant,
}; };
use crate::{codec, config, downlink, framelog, integration, maccommand, metalog, region}; use crate::{codec, config, downlink, framelog, integration, maccommand, metalog, region};
use chirpstack_api::{api, integration as integration_pb, internal, meta}; use chirpstack_api::{api, integration as integration_pb, internal, meta};
@ -555,7 +556,7 @@ impl Data {
let dev = self.device.as_mut().unwrap(); let dev = self.device.as_mut().unwrap();
let conf = config::get(); let conf = config::get();
if &dev.enabled_class == "B" || &dev.enabled_class == "C" { if dev.enabled_class == DeviceClass::B || dev.enabled_class == DeviceClass::C {
trace!("Setting scheduler_run_after for device"); trace!("Setting scheduler_run_after for device");
let scheduler_run_after = let scheduler_run_after =
Utc::now() + Duration::from_std(conf.network.scheduler.class_a_lock_duration)?; Utc::now() + Duration::from_std(conf.network.scheduler.class_a_lock_duration)?;
@ -708,22 +709,21 @@ impl Data {
let dp = self.device_profile.as_ref().unwrap(); let dp = self.device_profile.as_ref().unwrap();
let mut mode = match dp.supports_class_c { let mut mode = match dp.supports_class_c {
true => "C", true => DeviceClass::C,
false => "A", false => DeviceClass::A,
} };
.to_string();
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload { if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
let locked = pl.fhdr.f_ctrl.class_b; let locked = pl.fhdr.f_ctrl.class_b;
mode = match locked { mode = match locked {
true => "B".to_string(), true => DeviceClass::B,
false => mode, false => mode,
}; };
} }
// Update if the enabled class has changed. // Update if the enabled class has changed.
if dev.enabled_class != mode { if dev.enabled_class != mode {
*dev = device::set_enabled_class(&dev.dev_eui, &mode).await?; *dev = device::set_enabled_class(&dev.dev_eui, mode).await?;
} }
Ok(()) Ok(())

View File

@ -20,7 +20,10 @@ use crate::api::backend::get_async_receiver;
use crate::backend::{joinserver, keywrap, roaming}; use crate::backend::{joinserver, keywrap, roaming};
use crate::storage::device_session; use crate::storage::device_session;
use crate::storage::{ use crate::storage::{
application, device, device_keys, device_profile, device_queue, error::Error as StorageError, application,
device::{self, DeviceClass},
device_keys, device_profile, device_queue,
error::Error as StorageError,
metrics, tenant, metrics, tenant,
}; };
use crate::{ use crate::{
@ -843,9 +846,9 @@ impl JoinRequest {
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C. // LoRaWAN 1.1 devices send a mac-command when changing to Class-C.
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") { if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
*device = device::set_enabled_class(&device.dev_eui, "C").await?; *device = device::set_enabled_class(&device.dev_eui, DeviceClass::C).await?;
} else { } else {
*device = device::set_enabled_class(&device.dev_eui, "A").await?; *device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
} }
Ok(()) Ok(())
} }

View File

@ -7,8 +7,11 @@ use tracing::{span, trace, Instrument, Level};
use super::{helpers, UplinkFrameSet}; use super::{helpers, UplinkFrameSet};
use crate::backend::{joinserver, keywrap, roaming}; use crate::backend::{joinserver, keywrap, roaming};
use crate::storage::{ use crate::storage::{
application, device, device_keys, device_profile, device_queue, device_session, application,
error::Error as StorageError, metrics, tenant, device::{self, DeviceClass},
device_keys, device_profile, device_queue, device_session,
error::Error as StorageError,
metrics, tenant,
}; };
use crate::{config, devaddr::get_random_dev_addr, integration, metalog, region}; use crate::{config, devaddr::get_random_dev_addr, integration, metalog, region};
use backend::{PRStartAnsPayload, PRStartReqPayload}; use backend::{PRStartAnsPayload, PRStartReqPayload};
@ -647,9 +650,9 @@ impl JoinRequest {
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C. // LoRaWAN 1.1 devices send a mac-command when changing to Class-C.
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") { if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
*device = device::set_enabled_class(&device.dev_eui, "C").await?; *device = device::set_enabled_class(&device.dev_eui, DeviceClass::C).await?;
} else { } else {
*device = device::set_enabled_class(&device.dev_eui, "A").await?; *device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
} }
Ok(()) Ok(())
} }