From 41d00cb651a1ad745f2155b027545d601aa14211 Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Thu, 5 Oct 2023 13:05:53 +0100 Subject: [PATCH] Implement end-to-end app payload encryption. This implements end-to-end encryption between the end-device and end-application. The encrypted AppSKey or SessionKeyID is forwarded to the end-application which should be able to decrypt or request the AppSKey to decrypt the uplink payload. As well the end-application will be able to enqueue encrypted application payloads. Using this mechanism, ChirpStack will never have access to the uplink and downlink application-payloads. --- api/csharp/Chirpstack/api/Device.cs | 561 ++++++++++++-- api/csharp/Chirpstack/api/DeviceGrpc.cs | 86 ++- .../Chirpstack/integration/Integration.cs | 492 +++++++++++-- api/go/api/device.pb.go | 597 +++++++++------ api/go/api/device_grpc.pb.go | 43 ++ api/go/integration/integration.pb.go | 689 ++++++++++-------- api/grpc-web/api/device_grpc_web_pb.d.ts | 12 + api/grpc-web/api/device_grpc_web_pb.js | 61 ++ api/grpc-web/api/device_pb.d.ts | 40 + api/grpc-web/api/device_pb.js | 336 ++++++++- api/js/api/device_grpc_pb.d.ts | 5 + api/js/api/device_grpc_pb.js | 36 + api/js/api/device_pb.d.ts | 44 ++ api/js/api/device_pb.js | 336 ++++++++- api/js/integration/integration_pb.d.ts | 39 + api/js/integration/integration_pb.js | 312 +++++++- api/md/api/api.md | 38 +- api/proto/api/device.proto | 33 +- api/proto/integration/integration.proto | 24 + api/proto/internal/internal.proto | 3 + .../proto/chirpstack-api/api/device.proto | 33 +- .../integration/integration.proto | 24 + .../chirpstack-api/internal/internal.proto | 3 + .../src/chirpstack_api/api/device_pb2.py | 38 +- .../src/chirpstack_api/api/device_pb2.pyi | 18 +- .../src/chirpstack_api/api/device_pb2_grpc.py | 36 + .../integration/integration_pb2.py | 52 +- .../integration/integration_pb2.pyi | 22 +- api/rust/proto/chirpstack/api/device.proto | 33 +- .../chirpstack/integration/integration.proto | 24 + .../proto/chirpstack/internal/internal.proto | 3 + api/rust/src/integration.rs | 1 + .../down.sql | 2 + .../up.sql | 5 + chirpstack/src/api/device.rs | 94 ++- chirpstack/src/downlink/data.rs | 342 ++++++++- chirpstack/src/downlink/tx_ack.rs | 2 +- chirpstack/src/integration/mock.rs | 42 ++ chirpstack/src/storage/device_queue.rs | 53 +- chirpstack/src/storage/schema.rs | 28 + chirpstack/src/test/assert.rs | 19 + chirpstack/src/test/class_a_test.rs | 328 +++++++++ chirpstack/src/test/mod.rs | 1 + chirpstack/src/test/otaa_js_test.rs | 396 ++++++++++ chirpstack/src/test/otaa_pr_test.rs | 1 + chirpstack/src/uplink/data.rs | 95 ++- chirpstack/src/uplink/join.rs | 69 +- chirpstack/src/uplink/join_sns.rs | 51 +- ui/src/views/devices/DeviceQueue.tsx | 40 +- 49 files changed, 4859 insertions(+), 783 deletions(-) create mode 100644 chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/down.sql create mode 100644 chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/up.sql create mode 100644 chirpstack/src/test/otaa_js_test.rs diff --git a/api/csharp/Chirpstack/api/Device.cs b/api/csharp/Chirpstack/api/Device.cs index 5decef0b..617d3a0e 100644 --- a/api/csharp/Chirpstack/api/Device.cs +++ b/api/csharp/Chirpstack/api/Device.cs @@ -101,67 +101,73 @@ namespace Chirpstack.Api { "CzIOLmNvbW1vbi5NZXRyaWMSHgoGZ3dfc25yGAMgASgLMg4uY29tbW9uLk1l", "dHJpYxIrChNyeF9wYWNrZXRzX3Blcl9mcmVxGAQgASgLMg4uY29tbW9uLk1l", "dHJpYxIpChFyeF9wYWNrZXRzX3Blcl9kchgFIAEoCzIOLmNvbW1vbi5NZXRy", - "aWMSHgoGZXJyb3JzGAYgASgLMg4uY29tbW9uLk1ldHJpYyKwAQoPRGV2aWNl", + "aWMSHgoGZXJyb3JzGAYgASgLMg4uY29tbW9uLk1ldHJpYyLGAQoPRGV2aWNl", "UXVldWVJdGVtEgoKAmlkGAEgASgJEg8KB2Rldl9ldWkYAiABKAkSEQoJY29u", "ZmlybWVkGAMgASgIEg4KBmZfcG9ydBgEIAEoDRIMCgRkYXRhGAUgASgMEicK", "Bm9iamVjdBgGIAEoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3QSEgoKaXNf", - "cGVuZGluZxgHIAEoCBISCgpmX2NudF9kb3duGAggASgNIkkKHUVucXVldWVE", - "ZXZpY2VRdWV1ZUl0ZW1SZXF1ZXN0EigKCnF1ZXVlX2l0ZW0YASABKAsyFC5h", - "cGkuRGV2aWNlUXVldWVJdGVtIiwKHkVucXVldWVEZXZpY2VRdWV1ZUl0ZW1S", - "ZXNwb25zZRIKCgJpZBgBIAEoCSIqChdGbHVzaERldmljZVF1ZXVlUmVxdWVz", - "dBIPCgdkZXZfZXVpGAEgASgJIkEKGkdldERldmljZVF1ZXVlSXRlbXNSZXF1", - "ZXN0Eg8KB2Rldl9ldWkYASABKAkSEgoKY291bnRfb25seRgCIAEoCCJYChtH", - "ZXREZXZpY2VRdWV1ZUl0ZW1zUmVzcG9uc2USEwoLdG90YWxfY291bnQYASAB", - "KA0SJAoGcmVzdWx0GAIgAygLMhQuYXBpLkRldmljZVF1ZXVlSXRlbSIoChVG", - "bHVzaERldk5vbmNlc1JlcXVlc3QSDwoHZGV2X2V1aRgBIAEoCTLQEAoNRGV2", - "aWNlU2VydmljZRJTCgZDcmVhdGUSGC5hcGkuQ3JlYXRlRGV2aWNlUmVxdWVz", - "dBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIXgtPkkwIRIgwvYXBpL2Rldmlj", - "ZXM6ASoSVAoDR2V0EhUuYXBpLkdldERldmljZVJlcXVlc3QaFi5hcGkuR2V0", - "RGV2aWNlUmVzcG9uc2UiHoLT5JMCGBIWL2FwaS9kZXZpY2VzL3tkZXZfZXVp", - "fRJkCgZVcGRhdGUSGC5hcGkuVXBkYXRlRGV2aWNlUmVxdWVzdBoWLmdvb2ds", - "ZS5wcm90b2J1Zi5FbXB0eSIogtPkkwIiGh0vYXBpL2RldmljZXMve2Rldmlj", - "ZS5kZXZfZXVpfToBKhJaCgZEZWxldGUSGC5hcGkuRGVsZXRlRGV2aWNlUmVx", - "dWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIegtPkkwIYKhYvYXBpL2Rl", - "dmljZXMve2Rldl9ldWl9Ek8KBExpc3QSFy5hcGkuTGlzdERldmljZXNSZXF1", - "ZXN0GhguYXBpLkxpc3REZXZpY2VzUmVzcG9uc2UiFILT5JMCDhIML2FwaS9k", - "ZXZpY2VzEnYKCkNyZWF0ZUtleXMSHC5hcGkuQ3JlYXRlRGV2aWNlS2V5c1Jl", - "cXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiMoLT5JMCLCInL2FwaS9k", - "ZXZpY2VzL3tkZXZpY2Vfa2V5cy5kZXZfZXVpfS9rZXlzOgEqEmUKB0dldEtl", - "eXMSGS5hcGkuR2V0RGV2aWNlS2V5c1JlcXVlc3QaGi5hcGkuR2V0RGV2aWNl", - "S2V5c1Jlc3BvbnNlIiOC0+STAh0SGy9hcGkvZGV2aWNlcy97ZGV2X2V1aX0v", - "a2V5cxJ2CgpVcGRhdGVLZXlzEhwuYXBpLlVwZGF0ZURldmljZUtleXNSZXF1", - "ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjKC0+STAiwaJy9hcGkvZGV2", - "aWNlcy97ZGV2aWNlX2tleXMuZGV2X2V1aX0va2V5czoBKhJnCgpEZWxldGVL", - "ZXlzEhwuYXBpLkRlbGV0ZURldmljZUtleXNSZXF1ZXN0GhYuZ29vZ2xlLnBy", - "b3RvYnVmLkVtcHR5IiOC0+STAh0qGy9hcGkvZGV2aWNlcy97ZGV2X2V1aX0v", - "a2V5cxJvCg5GbHVzaERldk5vbmNlcxIaLmFwaS5GbHVzaERldk5vbmNlc1Jl", - "cXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiKYLT5JMCIyohL2FwaS9k", - "ZXZpY2VzL3tkZXZfZXVpfS9kZXYtbm9uY2VzEnwKCEFjdGl2YXRlEhouYXBp", - "LkFjdGl2YXRlRGV2aWNlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0", - "eSI8gtPkkwI2IjEvYXBpL2RldmljZXMve2RldmljZV9hY3RpdmF0aW9uLmRl", - "dl9ldWl9L2FjdGl2YXRlOgEqEm0KCkRlYWN0aXZhdGUSHC5hcGkuRGVhY3Rp", - "dmF0ZURldmljZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiKYLT", - "5JMCIyohL2FwaS9kZXZpY2VzL3tkZXZfZXVpfS9hY3RpdmF0aW9uEn0KDUdl", - "dEFjdGl2YXRpb24SHy5hcGkuR2V0RGV2aWNlQWN0aXZhdGlvblJlcXVlc3Qa", - "IC5hcGkuR2V0RGV2aWNlQWN0aXZhdGlvblJlc3BvbnNlIimC0+STAiMSIS9h", - "cGkvZGV2aWNlcy97ZGV2X2V1aX0vYWN0aXZhdGlvbhKDAQoQR2V0UmFuZG9t", - "RGV2QWRkchIcLmFwaS5HZXRSYW5kb21EZXZBZGRyUmVxdWVzdBodLmFwaS5H", - "ZXRSYW5kb21EZXZBZGRyUmVzcG9uc2UiMoLT5JMCLCIqL2FwaS9kZXZpY2Vz", - "L3tkZXZfZXVpfS9nZXQtcmFuZG9tLWRldi1hZGRyEnEKCkdldE1ldHJpY3MS", - "HC5hcGkuR2V0RGV2aWNlTWV0cmljc1JlcXVlc3QaHS5hcGkuR2V0RGV2aWNl", - "TWV0cmljc1Jlc3BvbnNlIiaC0+STAiASHi9hcGkvZGV2aWNlcy97ZGV2X2V1", - "aX0vbWV0cmljcxKCAQoOR2V0TGlua01ldHJpY3MSIC5hcGkuR2V0RGV2aWNl", - "TGlua01ldHJpY3NSZXF1ZXN0GiEuYXBpLkdldERldmljZUxpbmtNZXRyaWNz", - "UmVzcG9uc2UiK4LT5JMCJRIjL2FwaS9kZXZpY2VzL3tkZXZfZXVpfS9saW5r", - "LW1ldHJpY3MShgEKB0VucXVldWUSIi5hcGkuRW5xdWV1ZURldmljZVF1ZXVl", - "SXRlbVJlcXVlc3QaIy5hcGkuRW5xdWV1ZURldmljZVF1ZXVlSXRlbVJlc3Bv", - "bnNlIjKC0+STAiwiJy9hcGkvZGV2aWNlcy97cXVldWVfaXRlbS5kZXZfZXVp", - "fS9xdWV1ZToBKhJoCgpGbHVzaFF1ZXVlEhwuYXBpLkZsdXNoRGV2aWNlUXVl", - "dWVSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IiSC0+STAh4qHC9h", - "cGkvZGV2aWNlcy97ZGV2X2V1aX0vcXVldWUScwoIR2V0UXVldWUSHy5hcGku", - "R2V0RGV2aWNlUXVldWVJdGVtc1JlcXVlc3QaIC5hcGkuR2V0RGV2aWNlUXVl", - "dWVJdGVtc1Jlc3BvbnNlIiSC0+STAh4SHC9hcGkvZGV2aWNlcy97ZGV2X2V1", - "aX0vcXVldWVCYwoRaW8uY2hpcnBzdGFjay5hcGlCC0RldmljZVByb3RvUAFa", + "cGVuZGluZxgHIAEoCBISCgpmX2NudF9kb3duGAggASgNEhQKDGlzX2VuY3J5", + "cHRlZBgJIAEoCCJJCh1FbnF1ZXVlRGV2aWNlUXVldWVJdGVtUmVxdWVzdBIo", + "CgpxdWV1ZV9pdGVtGAEgASgLMhQuYXBpLkRldmljZVF1ZXVlSXRlbSIsCh5F", + "bnF1ZXVlRGV2aWNlUXVldWVJdGVtUmVzcG9uc2USCgoCaWQYASABKAkiKgoX", + "Rmx1c2hEZXZpY2VRdWV1ZVJlcXVlc3QSDwoHZGV2X2V1aRgBIAEoCSJBChpH", + "ZXREZXZpY2VRdWV1ZUl0ZW1zUmVxdWVzdBIPCgdkZXZfZXVpGAEgASgJEhIK", + "CmNvdW50X29ubHkYAiABKAgiWAobR2V0RGV2aWNlUXVldWVJdGVtc1Jlc3Bv", + "bnNlEhMKC3RvdGFsX2NvdW50GAEgASgNEiQKBnJlc3VsdBgCIAMoCzIULmFw", + "aS5EZXZpY2VRdWV1ZUl0ZW0iKAoVRmx1c2hEZXZOb25jZXNSZXF1ZXN0Eg8K", + "B2Rldl9ldWkYASABKAkiLwocR2V0RGV2aWNlTmV4dEZDbnREb3duUmVxdWVz", + "dBIPCgdkZXZfZXVpGAEgASgJIjMKHUdldERldmljZU5leHRGQ250RG93blJl", + "c3BvbnNlEhIKCmZfY250X2Rvd24YASABKA0y4hEKDURldmljZVNlcnZpY2US", + "UwoGQ3JlYXRlEhguYXBpLkNyZWF0ZURldmljZVJlcXVlc3QaFi5nb29nbGUu", + "cHJvdG9idWYuRW1wdHkiF4LT5JMCESIML2FwaS9kZXZpY2VzOgEqElQKA0dl", + "dBIVLmFwaS5HZXREZXZpY2VSZXF1ZXN0GhYuYXBpLkdldERldmljZVJlc3Bv", + "bnNlIh6C0+STAhgSFi9hcGkvZGV2aWNlcy97ZGV2X2V1aX0SZAoGVXBkYXRl", + "EhguYXBpLlVwZGF0ZURldmljZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYu", + "RW1wdHkiKILT5JMCIhodL2FwaS9kZXZpY2VzL3tkZXZpY2UuZGV2X2V1aX06", + "ASoSWgoGRGVsZXRlEhguYXBpLkRlbGV0ZURldmljZVJlcXVlc3QaFi5nb29n", + "bGUucHJvdG9idWYuRW1wdHkiHoLT5JMCGCoWL2FwaS9kZXZpY2VzL3tkZXZf", + "ZXVpfRJPCgRMaXN0EhcuYXBpLkxpc3REZXZpY2VzUmVxdWVzdBoYLmFwaS5M", + "aXN0RGV2aWNlc1Jlc3BvbnNlIhSC0+STAg4SDC9hcGkvZGV2aWNlcxJ2CgpD", + "cmVhdGVLZXlzEhwuYXBpLkNyZWF0ZURldmljZUtleXNSZXF1ZXN0GhYuZ29v", + "Z2xlLnByb3RvYnVmLkVtcHR5IjKC0+STAiwiJy9hcGkvZGV2aWNlcy97ZGV2", + "aWNlX2tleXMuZGV2X2V1aX0va2V5czoBKhJlCgdHZXRLZXlzEhkuYXBpLkdl", + "dERldmljZUtleXNSZXF1ZXN0GhouYXBpLkdldERldmljZUtleXNSZXNwb25z", + "ZSIjgtPkkwIdEhsvYXBpL2RldmljZXMve2Rldl9ldWl9L2tleXMSdgoKVXBk", + "YXRlS2V5cxIcLmFwaS5VcGRhdGVEZXZpY2VLZXlzUmVxdWVzdBoWLmdvb2ds", + "ZS5wcm90b2J1Zi5FbXB0eSIygtPkkwIsGicvYXBpL2RldmljZXMve2Rldmlj", + "ZV9rZXlzLmRldl9ldWl9L2tleXM6ASoSZwoKRGVsZXRlS2V5cxIcLmFwaS5E", + "ZWxldGVEZXZpY2VLZXlzUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0", + "eSIjgtPkkwIdKhsvYXBpL2RldmljZXMve2Rldl9ldWl9L2tleXMSbwoORmx1", + "c2hEZXZOb25jZXMSGi5hcGkuRmx1c2hEZXZOb25jZXNSZXF1ZXN0GhYuZ29v", + "Z2xlLnByb3RvYnVmLkVtcHR5IimC0+STAiMqIS9hcGkvZGV2aWNlcy97ZGV2", + "X2V1aX0vZGV2LW5vbmNlcxJ8CghBY3RpdmF0ZRIaLmFwaS5BY3RpdmF0ZURl", + "dmljZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiPILT5JMCNiIx", + "L2FwaS9kZXZpY2VzL3tkZXZpY2VfYWN0aXZhdGlvbi5kZXZfZXVpfS9hY3Rp", + "dmF0ZToBKhJtCgpEZWFjdGl2YXRlEhwuYXBpLkRlYWN0aXZhdGVEZXZpY2VS", + "ZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IimC0+STAiMqIS9hcGkv", + "ZGV2aWNlcy97ZGV2X2V1aX0vYWN0aXZhdGlvbhJ9Cg1HZXRBY3RpdmF0aW9u", + "Eh8uYXBpLkdldERldmljZUFjdGl2YXRpb25SZXF1ZXN0GiAuYXBpLkdldERl", + "dmljZUFjdGl2YXRpb25SZXNwb25zZSIpgtPkkwIjEiEvYXBpL2RldmljZXMv", + "e2Rldl9ldWl9L2FjdGl2YXRpb24SgwEKEEdldFJhbmRvbURldkFkZHISHC5h", + "cGkuR2V0UmFuZG9tRGV2QWRkclJlcXVlc3QaHS5hcGkuR2V0UmFuZG9tRGV2", + "QWRkclJlc3BvbnNlIjKC0+STAiwiKi9hcGkvZGV2aWNlcy97ZGV2X2V1aX0v", + "Z2V0LXJhbmRvbS1kZXYtYWRkchJxCgpHZXRNZXRyaWNzEhwuYXBpLkdldERl", + "dmljZU1ldHJpY3NSZXF1ZXN0Gh0uYXBpLkdldERldmljZU1ldHJpY3NSZXNw", + "b25zZSImgtPkkwIgEh4vYXBpL2RldmljZXMve2Rldl9ldWl9L21ldHJpY3MS", + "ggEKDkdldExpbmtNZXRyaWNzEiAuYXBpLkdldERldmljZUxpbmtNZXRyaWNz", + "UmVxdWVzdBohLmFwaS5HZXREZXZpY2VMaW5rTWV0cmljc1Jlc3BvbnNlIiuC", + "0+STAiUSIy9hcGkvZGV2aWNlcy97ZGV2X2V1aX0vbGluay1tZXRyaWNzEoYB", + "CgdFbnF1ZXVlEiIuYXBpLkVucXVldWVEZXZpY2VRdWV1ZUl0ZW1SZXF1ZXN0", + "GiMuYXBpLkVucXVldWVEZXZpY2VRdWV1ZUl0ZW1SZXNwb25zZSIygtPkkwIs", + "IicvYXBpL2RldmljZXMve3F1ZXVlX2l0ZW0uZGV2X2V1aX0vcXVldWU6ASoS", + "aAoKRmx1c2hRdWV1ZRIcLmFwaS5GbHVzaERldmljZVF1ZXVlUmVxdWVzdBoW", + "Lmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIkgtPkkwIeKhwvYXBpL2RldmljZXMv", + "e2Rldl9ldWl9L3F1ZXVlEnMKCEdldFF1ZXVlEh8uYXBpLkdldERldmljZVF1", + "ZXVlSXRlbXNSZXF1ZXN0GiAuYXBpLkdldERldmljZVF1ZXVlSXRlbXNSZXNw", + "b25zZSIkgtPkkwIeEhwvYXBpL2RldmljZXMve2Rldl9ldWl9L3F1ZXVlEo8B", + "Cg9HZXROZXh0RkNudERvd24SIS5hcGkuR2V0RGV2aWNlTmV4dEZDbnREb3du", + "UmVxdWVzdBoiLmFwaS5HZXREZXZpY2VOZXh0RkNudERvd25SZXNwb25zZSI1", + "gtPkkwIvIiovYXBpL2RldmljZXMve2Rldl9ldWl9L2dldC1uZXh0LWYtY250", + "LWRvd246ASpCYwoRaW8uY2hpcnBzdGFjay5hcGlCC0RldmljZVByb3RvUAFa", "LmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNrL2FwaS9nby92NC9h", "cGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, @@ -195,13 +201,15 @@ namespace Chirpstack.Api { new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceState), global::Chirpstack.Api.DeviceState.Parser, new[]{ "Name", "Value" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceLinkMetricsRequest), global::Chirpstack.Api.GetDeviceLinkMetricsRequest.Parser, new[]{ "DevEui", "Start", "End", "Aggregation" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceLinkMetricsResponse), global::Chirpstack.Api.GetDeviceLinkMetricsResponse.Parser, new[]{ "RxPackets", "GwRssi", "GwSnr", "RxPacketsPerFreq", "RxPacketsPerDr", "Errors" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceQueueItem), global::Chirpstack.Api.DeviceQueueItem.Parser, new[]{ "Id", "DevEui", "Confirmed", "FPort", "Data", "Object", "IsPending", "FCntDown" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceQueueItem), global::Chirpstack.Api.DeviceQueueItem.Parser, new[]{ "Id", "DevEui", "Confirmed", "FPort", "Data", "Object", "IsPending", "FCntDown", "IsEncrypted" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest), global::Chirpstack.Api.EnqueueDeviceQueueItemRequest.Parser, new[]{ "QueueItem" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueDeviceQueueItemResponse), global::Chirpstack.Api.EnqueueDeviceQueueItemResponse.Parser, new[]{ "Id" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushDeviceQueueRequest), global::Chirpstack.Api.FlushDeviceQueueRequest.Parser, new[]{ "DevEui" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceQueueItemsRequest), global::Chirpstack.Api.GetDeviceQueueItemsRequest.Parser, new[]{ "DevEui", "CountOnly" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceQueueItemsResponse), global::Chirpstack.Api.GetDeviceQueueItemsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushDevNoncesRequest), global::Chirpstack.Api.FlushDevNoncesRequest.Parser, new[]{ "DevEui" }, null, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushDevNoncesRequest), global::Chirpstack.Api.FlushDevNoncesRequest.Parser, new[]{ "DevEui" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceNextFCntDownRequest), global::Chirpstack.Api.GetDeviceNextFCntDownRequest.Parser, new[]{ "DevEui" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceNextFCntDownResponse), global::Chirpstack.Api.GetDeviceNextFCntDownResponse.Parser, new[]{ "FCntDown" }, null, null, null, null) })); } #endregion @@ -7991,6 +7999,7 @@ namespace Chirpstack.Api { object_ = other.object_ != null ? other.object_.Clone() : null; isPending_ = other.isPending_; fCntDown_ = other.fCntDown_; + isEncrypted_ = other.isEncrypted_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -8098,7 +8107,8 @@ namespace Chirpstack.Api { private bool isPending_; /// /// Is pending. - /// This is set to true when the downlink is pending. + /// This is set by ChirpStack to true when the downlink is pending (e.g. it + /// has been sent, but a confirmation is still pending). /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] @@ -8114,7 +8124,8 @@ namespace Chirpstack.Api { private uint fCntDown_; /// /// Downlink frame-counter. - /// This is set when the payload has been sent as downlink. + /// Do not set this for plain-text data payloads. It will be automatically set + /// by ChirpStack when the payload has been sent as downlink. /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] @@ -8125,6 +8136,24 @@ namespace Chirpstack.Api { } } + /// Field number for the "is_encrypted" field. + public const int IsEncryptedFieldNumber = 9; + private bool isEncrypted_; + /// + /// Is encrypted. + /// This must be set to true if the end-application has already encrypted + /// the data payload. In this case, the f_cnt_down field must be set to + /// the corresponding frame-counter which has been used during the encryption. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool IsEncrypted { + get { return isEncrypted_; } + set { + isEncrypted_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { @@ -8148,6 +8177,7 @@ namespace Chirpstack.Api { if (!object.Equals(Object, other.Object)) return false; if (IsPending != other.IsPending) return false; if (FCntDown != other.FCntDown) return false; + if (IsEncrypted != other.IsEncrypted) return false; return Equals(_unknownFields, other._unknownFields); } @@ -8163,6 +8193,7 @@ namespace Chirpstack.Api { if (object_ != null) hash ^= Object.GetHashCode(); if (IsPending != false) hash ^= IsPending.GetHashCode(); if (FCntDown != 0) hash ^= FCntDown.GetHashCode(); + if (IsEncrypted != false) hash ^= IsEncrypted.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -8213,6 +8244,10 @@ namespace Chirpstack.Api { output.WriteRawTag(64); output.WriteUInt32(FCntDown); } + if (IsEncrypted != false) { + output.WriteRawTag(72); + output.WriteBool(IsEncrypted); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -8255,6 +8290,10 @@ namespace Chirpstack.Api { output.WriteRawTag(64); output.WriteUInt32(FCntDown); } + if (IsEncrypted != false) { + output.WriteRawTag(72); + output.WriteBool(IsEncrypted); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -8289,6 +8328,9 @@ namespace Chirpstack.Api { if (FCntDown != 0) { size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntDown); } + if (IsEncrypted != false) { + size += 1 + 1; + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -8328,6 +8370,9 @@ namespace Chirpstack.Api { if (other.FCntDown != 0) { FCntDown = other.FCntDown; } + if (other.IsEncrypted != false) { + IsEncrypted = other.IsEncrypted; + } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -8378,6 +8423,10 @@ namespace Chirpstack.Api { FCntDown = input.ReadUInt32(); break; } + case 72: { + IsEncrypted = input.ReadBool(); + break; + } } } #endif @@ -8428,6 +8477,10 @@ namespace Chirpstack.Api { FCntDown = input.ReadUInt32(); break; } + case 72: { + IsEncrypted = input.ReadBool(); + break; + } } } } @@ -9662,6 +9715,390 @@ namespace Chirpstack.Api { } + public sealed partial class GetDeviceNextFCntDownRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new GetDeviceNextFCntDownRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[35]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownRequest(GetDeviceNextFCntDownRequest other) : this() { + devEui_ = other.devEui_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownRequest Clone() { + return new GetDeviceNextFCntDownRequest(this); + } + + /// Field number for the "dev_eui" field. + public const int DevEuiFieldNumber = 1; + private string devEui_ = ""; + /// + /// Device EUI (EUI64). + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string DevEui { + get { return devEui_; } + set { + devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as GetDeviceNextFCntDownRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(GetDeviceNextFCntDownRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (DevEui != other.DevEui) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (DevEui.Length != 0) hash ^= DevEui.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (DevEui.Length != 0) { + output.WriteRawTag(10); + output.WriteString(DevEui); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (DevEui.Length != 0) { + output.WriteRawTag(10); + output.WriteString(DevEui); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (DevEui.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(GetDeviceNextFCntDownRequest other) { + if (other == null) { + return; + } + if (other.DevEui.Length != 0) { + DevEui = other.DevEui; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + DevEui = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + DevEui = input.ReadString(); + break; + } + } + } + } + #endif + + } + + public sealed partial class GetDeviceNextFCntDownResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new GetDeviceNextFCntDownResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[36]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownResponse(GetDeviceNextFCntDownResponse other) : this() { + fCntDown_ = other.fCntDown_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public GetDeviceNextFCntDownResponse Clone() { + return new GetDeviceNextFCntDownResponse(this); + } + + /// Field number for the "f_cnt_down" field. + public const int FCntDownFieldNumber = 1; + private uint fCntDown_; + /// + /// FCntDown. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint FCntDown { + get { return fCntDown_; } + set { + fCntDown_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as GetDeviceNextFCntDownResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(GetDeviceNextFCntDownResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (FCntDown != other.FCntDown) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (FCntDown != 0) hash ^= FCntDown.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (FCntDown != 0) { + output.WriteRawTag(8); + output.WriteUInt32(FCntDown); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (FCntDown != 0) { + output.WriteRawTag(8); + output.WriteUInt32(FCntDown); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (FCntDown != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntDown); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(GetDeviceNextFCntDownResponse other) { + if (other == null) { + return; + } + if (other.FCntDown != 0) { + FCntDown = other.FCntDown; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + FCntDown = input.ReadUInt32(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + FCntDown = input.ReadUInt32(); + break; + } + } + } + } + #endif + + } + #endregion } diff --git a/api/csharp/Chirpstack/api/DeviceGrpc.cs b/api/csharp/Chirpstack/api/DeviceGrpc.cs index 8e48178d..6530d065 100644 --- a/api/csharp/Chirpstack/api/DeviceGrpc.cs +++ b/api/csharp/Chirpstack/api/DeviceGrpc.cs @@ -106,6 +106,10 @@ namespace Chirpstack.Api { static readonly grpc::Marshaller __Marshaller_api_GetDeviceQueueItemsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceQueueItemsRequest.Parser)); [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] static readonly grpc::Marshaller __Marshaller_api_GetDeviceQueueItemsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceQueueItemsResponse.Parser)); + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + static readonly grpc::Marshaller __Marshaller_api_GetDeviceNextFCntDownRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceNextFCntDownRequest.Parser)); + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + static readonly grpc::Marshaller __Marshaller_api_GetDeviceNextFCntDownResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceNextFCntDownResponse.Parser)); [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] static readonly grpc::Method __Method_Create = new grpc::Method( @@ -259,6 +263,14 @@ namespace Chirpstack.Api { __Marshaller_api_GetDeviceQueueItemsRequest, __Marshaller_api_GetDeviceQueueItemsResponse); + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + static readonly grpc::Method __Method_GetNextFCntDown = new grpc::Method( + grpc::MethodType.Unary, + __ServiceName, + "GetNextFCntDown", + __Marshaller_api_GetDeviceNextFCntDownRequest, + __Marshaller_api_GetDeviceNextFCntDownResponse); + /// Service descriptor public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor { @@ -503,6 +515,20 @@ namespace Chirpstack.Api { throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); } + /// + /// GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + /// downlinks. The difference with the DeviceActivation f_cont_down is that + /// this method takes potential existing queue-items into account. + /// + /// The request received from the client. + /// The context of the server-side call handler being invoked. + /// The response to send back to the client (wrapped by a task). + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + public virtual global::System.Threading.Tasks.Task GetNextFCntDown(global::Chirpstack.Api.GetDeviceNextFCntDownRequest request, grpc::ServerCallContext context) + { + throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); + } + } /// Client for DeviceService @@ -1468,6 +1494,62 @@ namespace Chirpstack.Api { { return CallInvoker.AsyncUnaryCall(__Method_GetQueue, null, options, request); } + /// + /// GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + /// downlinks. The difference with the DeviceActivation f_cont_down is that + /// this method takes potential existing queue-items into account. + /// + /// The request to send to the server. + /// The initial metadata to send with the call. This parameter is optional. + /// An optional deadline for the call. The call will be cancelled if deadline is hit. + /// An optional token for canceling the call. + /// The response received from the server. + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + public virtual global::Chirpstack.Api.GetDeviceNextFCntDownResponse GetNextFCntDown(global::Chirpstack.Api.GetDeviceNextFCntDownRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return GetNextFCntDown(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + /// + /// GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + /// downlinks. The difference with the DeviceActivation f_cont_down is that + /// this method takes potential existing queue-items into account. + /// + /// The request to send to the server. + /// The options for the call. + /// The response received from the server. + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + public virtual global::Chirpstack.Api.GetDeviceNextFCntDownResponse GetNextFCntDown(global::Chirpstack.Api.GetDeviceNextFCntDownRequest request, grpc::CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_GetNextFCntDown, null, options, request); + } + /// + /// GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + /// downlinks. The difference with the DeviceActivation f_cont_down is that + /// this method takes potential existing queue-items into account. + /// + /// The request to send to the server. + /// The initial metadata to send with the call. This parameter is optional. + /// An optional deadline for the call. The call will be cancelled if deadline is hit. + /// An optional token for canceling the call. + /// The call object. + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + public virtual grpc::AsyncUnaryCall GetNextFCntDownAsync(global::Chirpstack.Api.GetDeviceNextFCntDownRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return GetNextFCntDownAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + /// + /// GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + /// downlinks. The difference with the DeviceActivation f_cont_down is that + /// this method takes potential existing queue-items into account. + /// + /// The request to send to the server. + /// The options for the call. + /// The call object. + [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] + public virtual grpc::AsyncUnaryCall GetNextFCntDownAsync(global::Chirpstack.Api.GetDeviceNextFCntDownRequest request, grpc::CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_GetNextFCntDown, null, options, request); + } /// Creates a new instance of client from given ClientBaseConfiguration. [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] protected override DeviceServiceClient NewInstance(ClientBaseConfiguration configuration) @@ -1500,7 +1582,8 @@ namespace Chirpstack.Api { .AddMethod(__Method_GetLinkMetrics, serviceImpl.GetLinkMetrics) .AddMethod(__Method_Enqueue, serviceImpl.Enqueue) .AddMethod(__Method_FlushQueue, serviceImpl.FlushQueue) - .AddMethod(__Method_GetQueue, serviceImpl.GetQueue).Build(); + .AddMethod(__Method_GetQueue, serviceImpl.GetQueue) + .AddMethod(__Method_GetNextFCntDown, serviceImpl.GetNextFCntDown).Build(); } /// Register service method with a service binder with or without implementation. Useful when customizing the service binding logic. @@ -1529,6 +1612,7 @@ namespace Chirpstack.Api { serviceBinder.AddMethod(__Method_Enqueue, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Enqueue)); serviceBinder.AddMethod(__Method_FlushQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.FlushQueue)); serviceBinder.AddMethod(__Method_GetQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.GetQueue)); + serviceBinder.AddMethod(__Method_GetNextFCntDown, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.GetNextFCntDown)); } } diff --git a/api/csharp/Chirpstack/integration/Integration.cs b/api/csharp/Chirpstack/integration/Integration.cs index 5aafeb25..c03d17a7 100644 --- a/api/csharp/Chirpstack/integration/Integration.cs +++ b/api/csharp/Chirpstack/integration/Integration.cs @@ -37,69 +37,75 @@ namespace Chirpstack.Integration { "dHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEicwoRVXBsaW5r", "UmVsYXlSeEluZm8SDwoHZGV2X2V1aRgBIAEoCRIRCglmcmVxdWVuY3kYAiAB", "KA0SCgoCZHIYAyABKA0SCwoDc25yGAQgASgFEgwKBHJzc2kYBSABKAUSEwoL", - "d29yX2NoYW5uZWwYBiABKA0ikAMKC1VwbGlua0V2ZW50EhgKEGRlZHVwbGlj", - "YXRpb25faWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1", - "Zi5UaW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsyFy5pbnRlZ3JhdGlv", - "bi5EZXZpY2VJbmZvEhAKCGRldl9hZGRyGAQgASgJEgsKA2FkchgFIAEoCBIK", - "CgJkchgGIAEoDRINCgVmX2NudBgHIAEoDRIOCgZmX3BvcnQYCCABKA0SEQoJ", - "Y29uZmlybWVkGAkgASgIEgwKBGRhdGEYCiABKAwSJwoGb2JqZWN0GAsgASgL", - "MhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdBIhCgdyeF9pbmZvGAwgAygLMhAu", - "Z3cuVXBsaW5rUnhJbmZvEiEKB3R4X2luZm8YDSABKAsyEC5ndy5VcGxpbmtU", - "eEluZm8SNQoNcmVsYXlfcnhfaW5mbxgOIAEoCzIeLmludGVncmF0aW9uLlVw", - "bGlua1JlbGF5UnhJbmZvIsYBCglKb2luRXZlbnQSGAoQZGVkdXBsaWNhdGlv", - "bl9pZBgBIAEoCRIoCgR0aW1lGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRp", - "bWVzdGFtcBIsCgtkZXZpY2VfaW5mbxgDIAEoCzIXLmludGVncmF0aW9uLkRl", - "dmljZUluZm8SEAoIZGV2X2FkZHIYBCABKAkSNQoNcmVsYXlfcnhfaW5mbxgF", - "IAEoCzIeLmludGVncmF0aW9uLlVwbGlua1JlbGF5UnhJbmZvIr0BCghBY2tF", - "dmVudBIYChBkZWR1cGxpY2F0aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsy", - "Gi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMg", - "ASgLMhcuaW50ZWdyYXRpb24uRGV2aWNlSW5mbxIVCg1xdWV1ZV9pdGVtX2lk", - "GAQgASgJEhQKDGFja25vd2xlZGdlZBgFIAEoCBISCgpmX2NudF9kb3duGAYg", - "ASgNIt0BCgpUeEFja0V2ZW50EhMKC2Rvd25saW5rX2lkGAEgASgNEigKBHRp", - "bWUYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiwKC2Rldmlj", - "ZV9pbmZvGAMgASgLMhcuaW50ZWdyYXRpb24uRGV2aWNlSW5mbxIVCg1xdWV1", - "ZV9pdGVtX2lkGAQgASgJEhIKCmZfY250X2Rvd24YBSABKA0SEgoKZ2F0ZXdh", - "eV9pZBgGIAEoCRIjCgd0eF9pbmZvGAcgASgLMhIuZ3cuRG93bmxpbmtUeElu", - "Zm8ipgIKCExvZ0V2ZW50EigKBHRpbWUYASABKAsyGi5nb29nbGUucHJvdG9i", - "dWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAIgASgLMhcuaW50ZWdyYXRp", - "b24uRGV2aWNlSW5mbxIkCgVsZXZlbBgDIAEoDjIVLmludGVncmF0aW9uLkxv", - "Z0xldmVsEiIKBGNvZGUYBCABKA4yFC5pbnRlZ3JhdGlvbi5Mb2dDb2RlEhMK", - "C2Rlc2NyaXB0aW9uGAUgASgJEjMKB2NvbnRleHQYBiADKAsyIi5pbnRlZ3Jh", - "dGlvbi5Mb2dFdmVudC5Db250ZXh0RW50cnkaLgoMQ29udGV4dEVudHJ5EgsK", - "A2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEi6AEKC1N0YXR1c0V2ZW50", - "EhgKEGRlZHVwbGljYXRpb25faWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdv", - "b2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsy", - "Fy5pbnRlZ3JhdGlvbi5EZXZpY2VJbmZvEg4KBm1hcmdpbhgFIAEoBRIdChVl", - "eHRlcm5hbF9wb3dlcl9zb3VyY2UYBiABKAgSIQoZYmF0dGVyeV9sZXZlbF91", - "bmF2YWlsYWJsZRgHIAEoCBIVCg1iYXR0ZXJ5X2xldmVsGAggASgCIqUBCg1M", - "b2NhdGlvbkV2ZW50EhgKEGRlZHVwbGljYXRpb25faWQYASABKAkSKAoEdGlt", - "ZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLAoLZGV2aWNl", - "X2luZm8YAyABKAsyFy5pbnRlZ3JhdGlvbi5EZXZpY2VJbmZvEiIKCGxvY2F0", - "aW9uGAQgASgLMhAuY29tbW9uLkxvY2F0aW9uItsBChBJbnRlZ3JhdGlvbkV2", - "ZW50EhgKEGRlZHVwbGljYXRpb25faWQYASABKAkSKAoEdGltZRgCIAEoCzIa", - "Lmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyAB", - "KAsyFy5pbnRlZ3JhdGlvbi5EZXZpY2VJbmZvEhgKEGludGVncmF0aW9uX25h", - "bWUYBCABKAkSEgoKZXZlbnRfdHlwZRgFIAEoCRInCgZvYmplY3QYBiABKAsy", - "Fy5nb29nbGUucHJvdG9idWYuU3RydWN0IogBCg9Eb3dubGlua0NvbW1hbmQS", - "CgoCaWQYASABKAkSDwoHZGV2X2V1aRgCIAEoCRIRCgljb25maXJtZWQYAyAB", - "KAgSDgoGZl9wb3J0GAQgASgNEgwKBGRhdGEYBSABKAwSJwoGb2JqZWN0GAYg", - "ASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdCosCghMb2dMZXZlbBIICgRJ", - "TkZPEAASCwoHV0FSTklORxABEgkKBUVSUk9SEAIq2gEKB0xvZ0NvZGUSCwoH", - "VU5LTk9XThAAEhkKFURPV05MSU5LX1BBWUxPQURfU0laRRABEhAKDFVQTElO", - "S19DT0RFQxACEhIKDkRPV05MSU5LX0NPREVDEAMSCAoET1RBQRAEEhYKElVQ", - "TElOS19GX0NOVF9SRVNFVBAFEg4KClVQTElOS19NSUMQBhIfChtVUExJTktf", - "Rl9DTlRfUkVUUkFOU01JU1NJT04QBxIUChBET1dOTElOS19HQVRFV0FZEAgS", - "GAoUUkVMQVlfTkVXX0VORF9ERVZJQ0UQCUKBAQodaW8uY2hpcnBzdGFjay5h", - "cGkuaW50ZWdyYXRpb25CEEludGVncmF0aW9uUHJvdG9QAVozZ2l0aHViLmNv", - "bS9icm9jYWFyL2NoaXJwc3RhY2svYXBpL2dvL3Y0L2ludGVncmF0aW9uqgIW", - "Q2hpcnBzdGFjay5JbnRlZ3JhdGlvbmIGcHJvdG8z")); + "d29yX2NoYW5uZWwYBiABKA0iUwoRSm9pblNlcnZlckNvbnRleHQSFgoOc2Vz", + "c2lvbl9rZXlfaWQYASABKAkSJgoJYXBwX3Nfa2V5GAIgASgLMhMuY29tbW9u", + "LktleUVudmVsb3BlIs0DCgtVcGxpbmtFdmVudBIYChBkZWR1cGxpY2F0aW9u", + "X2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGlt", + "ZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50ZWdyYXRpb24uRGV2", + "aWNlSW5mbxIQCghkZXZfYWRkchgEIAEoCRILCgNhZHIYBSABKAgSCgoCZHIY", + "BiABKA0SDQoFZl9jbnQYByABKA0SDgoGZl9wb3J0GAggASgNEhEKCWNvbmZp", + "cm1lZBgJIAEoCBIMCgRkYXRhGAogASgMEicKBm9iamVjdBgLIAEoCzIXLmdv", + "b2dsZS5wcm90b2J1Zi5TdHJ1Y3QSIQoHcnhfaW5mbxgMIAMoCzIQLmd3LlVw", + "bGlua1J4SW5mbxIhCgd0eF9pbmZvGA0gASgLMhAuZ3cuVXBsaW5rVHhJbmZv", + "EjUKDXJlbGF5X3J4X2luZm8YDiABKAsyHi5pbnRlZ3JhdGlvbi5VcGxpbmtS", + "ZWxheVJ4SW5mbxI7ChNqb2luX3NlcnZlcl9jb250ZXh0GA8gASgLMh4uaW50", + "ZWdyYXRpb24uSm9pblNlcnZlckNvbnRleHQigwIKCUpvaW5FdmVudBIYChBk", + "ZWR1cGxpY2F0aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUu", + "cHJvdG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50", + "ZWdyYXRpb24uRGV2aWNlSW5mbxIQCghkZXZfYWRkchgEIAEoCRI1Cg1yZWxh", + "eV9yeF9pbmZvGAUgASgLMh4uaW50ZWdyYXRpb24uVXBsaW5rUmVsYXlSeElu", + "Zm8SOwoTam9pbl9zZXJ2ZXJfY29udGV4dBgGIAEoCzIeLmludGVncmF0aW9u", + "LkpvaW5TZXJ2ZXJDb250ZXh0Ir0BCghBY2tFdmVudBIYChBkZWR1cGxpY2F0", + "aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUucHJvdG9idWYu", + "VGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50ZWdyYXRpb24u", + "RGV2aWNlSW5mbxIVCg1xdWV1ZV9pdGVtX2lkGAQgASgJEhQKDGFja25vd2xl", + "ZGdlZBgFIAEoCBISCgpmX2NudF9kb3duGAYgASgNIt0BCgpUeEFja0V2ZW50", + "EhMKC2Rvd25saW5rX2lkGAEgASgNEigKBHRpbWUYAiABKAsyGi5nb29nbGUu", + "cHJvdG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50", + "ZWdyYXRpb24uRGV2aWNlSW5mbxIVCg1xdWV1ZV9pdGVtX2lkGAQgASgJEhIK", + "CmZfY250X2Rvd24YBSABKA0SEgoKZ2F0ZXdheV9pZBgGIAEoCRIjCgd0eF9p", + "bmZvGAcgASgLMhIuZ3cuRG93bmxpbmtUeEluZm8ipgIKCExvZ0V2ZW50EigK", + "BHRpbWUYASABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiwKC2Rl", + "dmljZV9pbmZvGAIgASgLMhcuaW50ZWdyYXRpb24uRGV2aWNlSW5mbxIkCgVs", + "ZXZlbBgDIAEoDjIVLmludGVncmF0aW9uLkxvZ0xldmVsEiIKBGNvZGUYBCAB", + "KA4yFC5pbnRlZ3JhdGlvbi5Mb2dDb2RlEhMKC2Rlc2NyaXB0aW9uGAUgASgJ", + "EjMKB2NvbnRleHQYBiADKAsyIi5pbnRlZ3JhdGlvbi5Mb2dFdmVudC5Db250", + "ZXh0RW50cnkaLgoMQ29udGV4dEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1", + "ZRgCIAEoCToCOAEi6AEKC1N0YXR1c0V2ZW50EhgKEGRlZHVwbGljYXRpb25f", + "aWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1l", + "c3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsyFy5pbnRlZ3JhdGlvbi5EZXZp", + "Y2VJbmZvEg4KBm1hcmdpbhgFIAEoBRIdChVleHRlcm5hbF9wb3dlcl9zb3Vy", + "Y2UYBiABKAgSIQoZYmF0dGVyeV9sZXZlbF91bmF2YWlsYWJsZRgHIAEoCBIV", + "Cg1iYXR0ZXJ5X2xldmVsGAggASgCIqUBCg1Mb2NhdGlvbkV2ZW50EhgKEGRl", + "ZHVwbGljYXRpb25faWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdvb2dsZS5w", + "cm90b2J1Zi5UaW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsyFy5pbnRl", + "Z3JhdGlvbi5EZXZpY2VJbmZvEiIKCGxvY2F0aW9uGAQgASgLMhAuY29tbW9u", + "LkxvY2F0aW9uItsBChBJbnRlZ3JhdGlvbkV2ZW50EhgKEGRlZHVwbGljYXRp", + "b25faWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5U", + "aW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsyFy5pbnRlZ3JhdGlvbi5E", + "ZXZpY2VJbmZvEhgKEGludGVncmF0aW9uX25hbWUYBCABKAkSEgoKZXZlbnRf", + "dHlwZRgFIAEoCRInCgZvYmplY3QYBiABKAsyFy5nb29nbGUucHJvdG9idWYu", + "U3RydWN0IogBCg9Eb3dubGlua0NvbW1hbmQSCgoCaWQYASABKAkSDwoHZGV2", + "X2V1aRgCIAEoCRIRCgljb25maXJtZWQYAyABKAgSDgoGZl9wb3J0GAQgASgN", + "EgwKBGRhdGEYBSABKAwSJwoGb2JqZWN0GAYgASgLMhcuZ29vZ2xlLnByb3Rv", + "YnVmLlN0cnVjdCosCghMb2dMZXZlbBIICgRJTkZPEAASCwoHV0FSTklORxAB", + "EgkKBUVSUk9SEAIq6gEKB0xvZ0NvZGUSCwoHVU5LTk9XThAAEhkKFURPV05M", + "SU5LX1BBWUxPQURfU0laRRABEhAKDFVQTElOS19DT0RFQxACEhIKDkRPV05M", + "SU5LX0NPREVDEAMSCAoET1RBQRAEEhYKElVQTElOS19GX0NOVF9SRVNFVBAF", + "Eg4KClVQTElOS19NSUMQBhIfChtVUExJTktfRl9DTlRfUkVUUkFOU01JU1NJ", + "T04QBxIUChBET1dOTElOS19HQVRFV0FZEAgSGAoUUkVMQVlfTkVXX0VORF9E", + "RVZJQ0UQCRIOCgpGX0NOVF9ET1dOEApCgQEKHWlvLmNoaXJwc3RhY2suYXBp", + "LmludGVncmF0aW9uQhBJbnRlZ3JhdGlvblByb3RvUAFaM2dpdGh1Yi5jb20v", + "YnJvY2Fhci9jaGlycHN0YWNrL2FwaS9nby92NC9pbnRlZ3JhdGlvbqoCFkNo", + "aXJwc3RhY2suSW50ZWdyYXRpb25iBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Gateway.GwReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Integration.LogLevel), typeof(global::Chirpstack.Integration.LogCode), }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.DeviceInfo), global::Chirpstack.Integration.DeviceInfo.Parser, new[]{ "TenantId", "TenantName", "ApplicationId", "ApplicationName", "DeviceProfileId", "DeviceProfileName", "DeviceName", "DevEui", "DeviceClassEnabled", "Tags" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.UplinkRelayRxInfo), global::Chirpstack.Integration.UplinkRelayRxInfo.Parser, new[]{ "DevEui", "Frequency", "Dr", "Snr", "Rssi", "WorChannel" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.UplinkEvent), global::Chirpstack.Integration.UplinkEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr", "Adr", "Dr", "FCnt", "FPort", "Confirmed", "Data", "Object", "RxInfo", "TxInfo", "RelayRxInfo" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.JoinEvent), global::Chirpstack.Integration.JoinEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr", "RelayRxInfo" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.JoinServerContext), global::Chirpstack.Integration.JoinServerContext.Parser, new[]{ "SessionKeyId", "AppSKey" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.UplinkEvent), global::Chirpstack.Integration.UplinkEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr", "Adr", "Dr", "FCnt", "FPort", "Confirmed", "Data", "Object", "RxInfo", "TxInfo", "RelayRxInfo", "JoinServerContext" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.JoinEvent), global::Chirpstack.Integration.JoinEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr", "RelayRxInfo", "JoinServerContext" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.AckEvent), global::Chirpstack.Integration.AckEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "QueueItemId", "Acknowledged", "FCntDown" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.TxAckEvent), global::Chirpstack.Integration.TxAckEvent.Parser, new[]{ "DownlinkId", "Time", "DeviceInfo", "QueueItemId", "FCntDown", "GatewayId", "TxInfo" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.LogEvent), global::Chirpstack.Integration.LogEvent.Parser, new[]{ "Time", "DeviceInfo", "Level", "Code", "Description", "Context" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }), @@ -170,6 +176,10 @@ namespace Chirpstack.Integration { /// Relay new end-device. /// [pbr::OriginalName("RELAY_NEW_END_DEVICE")] RelayNewEndDevice = 9, + /// + /// Downlink frame-counter. + /// + [pbr::OriginalName("F_CNT_DOWN")] FCntDown = 10, } #endregion @@ -1114,6 +1124,250 @@ namespace Chirpstack.Integration { } + /// + /// Join-Server context. + /// + public sealed partial class JoinServerContext : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new JoinServerContext()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[2]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public JoinServerContext() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public JoinServerContext(JoinServerContext other) : this() { + sessionKeyId_ = other.sessionKeyId_; + appSKey_ = other.appSKey_ != null ? other.appSKey_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public JoinServerContext Clone() { + return new JoinServerContext(this); + } + + /// Field number for the "session_key_id" field. + public const int SessionKeyIdFieldNumber = 1; + private string sessionKeyId_ = ""; + /// + /// Session-key ID. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string SessionKeyId { + get { return sessionKeyId_; } + set { + sessionKeyId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "app_s_key" field. + public const int AppSKeyFieldNumber = 2; + private global::Chirpstack.Common.KeyEnvelope appSKey_; + /// + /// AppSKey envelope. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Chirpstack.Common.KeyEnvelope AppSKey { + get { return appSKey_; } + set { + appSKey_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as JoinServerContext); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(JoinServerContext other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (SessionKeyId != other.SessionKeyId) return false; + if (!object.Equals(AppSKey, other.AppSKey)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (SessionKeyId.Length != 0) hash ^= SessionKeyId.GetHashCode(); + if (appSKey_ != null) hash ^= AppSKey.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (SessionKeyId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(SessionKeyId); + } + if (appSKey_ != null) { + output.WriteRawTag(18); + output.WriteMessage(AppSKey); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (SessionKeyId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(SessionKeyId); + } + if (appSKey_ != null) { + output.WriteRawTag(18); + output.WriteMessage(AppSKey); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (SessionKeyId.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(SessionKeyId); + } + if (appSKey_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(AppSKey); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(JoinServerContext other) { + if (other == null) { + return; + } + if (other.SessionKeyId.Length != 0) { + SessionKeyId = other.SessionKeyId; + } + if (other.appSKey_ != null) { + if (appSKey_ == null) { + AppSKey = new global::Chirpstack.Common.KeyEnvelope(); + } + AppSKey.MergeFrom(other.AppSKey); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + SessionKeyId = input.ReadString(); + break; + } + case 18: { + if (appSKey_ == null) { + AppSKey = new global::Chirpstack.Common.KeyEnvelope(); + } + input.ReadMessage(AppSKey); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + SessionKeyId = input.ReadString(); + break; + } + case 18: { + if (appSKey_ == null) { + AppSKey = new global::Chirpstack.Common.KeyEnvelope(); + } + input.ReadMessage(AppSKey); + break; + } + } + } + } + #endif + + } + /// /// UplinkEvent is the message sent when an uplink payload has been received. /// @@ -1131,7 +1385,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[2]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[3]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1165,6 +1419,7 @@ namespace Chirpstack.Integration { rxInfo_ = other.rxInfo_.Clone(); txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null; relayRxInfo_ = other.relayRxInfo_ != null ? other.relayRxInfo_.Clone() : null; + joinServerContext_ = other.joinServerContext_ != null ? other.joinServerContext_.Clone() : null; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -1384,6 +1639,24 @@ namespace Chirpstack.Integration { } } + /// Field number for the "join_server_context" field. + public const int JoinServerContextFieldNumber = 15; + private global::Chirpstack.Integration.JoinServerContext joinServerContext_; + /// + /// Join-Server context. + /// A non-empty value indicatest that ChirpStack does not have access to + /// the AppSKey and that the encryption / decryption of the payloads is + /// the responsibility of the end-application. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Chirpstack.Integration.JoinServerContext JoinServerContext { + get { return joinServerContext_; } + set { + joinServerContext_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { @@ -1413,6 +1686,7 @@ namespace Chirpstack.Integration { if(!rxInfo_.Equals(other.rxInfo_)) return false; if (!object.Equals(TxInfo, other.TxInfo)) return false; if (!object.Equals(RelayRxInfo, other.RelayRxInfo)) return false; + if (!object.Equals(JoinServerContext, other.JoinServerContext)) return false; return Equals(_unknownFields, other._unknownFields); } @@ -1434,6 +1708,7 @@ namespace Chirpstack.Integration { hash ^= rxInfo_.GetHashCode(); if (txInfo_ != null) hash ^= TxInfo.GetHashCode(); if (relayRxInfo_ != null) hash ^= RelayRxInfo.GetHashCode(); + if (joinServerContext_ != null) hash ^= JoinServerContext.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -1505,6 +1780,10 @@ namespace Chirpstack.Integration { output.WriteRawTag(114); output.WriteMessage(RelayRxInfo); } + if (joinServerContext_ != null) { + output.WriteRawTag(122); + output.WriteMessage(JoinServerContext); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -1568,6 +1847,10 @@ namespace Chirpstack.Integration { output.WriteRawTag(114); output.WriteMessage(RelayRxInfo); } + if (joinServerContext_ != null) { + output.WriteRawTag(122); + output.WriteMessage(JoinServerContext); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -1618,6 +1901,9 @@ namespace Chirpstack.Integration { if (relayRxInfo_ != null) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(RelayRxInfo); } + if (joinServerContext_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(JoinServerContext); + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -1685,6 +1971,12 @@ namespace Chirpstack.Integration { } RelayRxInfo.MergeFrom(other.RelayRxInfo); } + if (other.joinServerContext_ != null) { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + JoinServerContext.MergeFrom(other.JoinServerContext); + } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -1771,6 +2063,13 @@ namespace Chirpstack.Integration { input.ReadMessage(RelayRxInfo); break; } + case 122: { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + input.ReadMessage(JoinServerContext); + break; + } } } #endif @@ -1857,6 +2156,13 @@ namespace Chirpstack.Integration { input.ReadMessage(RelayRxInfo); break; } + case 122: { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + input.ReadMessage(JoinServerContext); + break; + } } } } @@ -1882,7 +2188,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[3]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[4]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1907,6 +2213,7 @@ namespace Chirpstack.Integration { deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null; devAddr_ = other.devAddr_; relayRxInfo_ = other.relayRxInfo_ != null ? other.relayRxInfo_.Clone() : null; + joinServerContext_ = other.joinServerContext_ != null ? other.joinServerContext_.Clone() : null; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -1991,6 +2298,24 @@ namespace Chirpstack.Integration { } } + /// Field number for the "join_server_context" field. + public const int JoinServerContextFieldNumber = 6; + private global::Chirpstack.Integration.JoinServerContext joinServerContext_; + /// + /// Join-Server context. + /// A non-empty value indicatest that ChirpStack does not have access to + /// the AppSKey and that the encryption / decryption of the payloads is + /// the responsibility of the end-application. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Chirpstack.Integration.JoinServerContext JoinServerContext { + get { return joinServerContext_; } + set { + joinServerContext_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { @@ -2011,6 +2336,7 @@ namespace Chirpstack.Integration { if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false; if (DevAddr != other.DevAddr) return false; if (!object.Equals(RelayRxInfo, other.RelayRxInfo)) return false; + if (!object.Equals(JoinServerContext, other.JoinServerContext)) return false; return Equals(_unknownFields, other._unknownFields); } @@ -2023,6 +2349,7 @@ namespace Chirpstack.Integration { if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode(); if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode(); if (relayRxInfo_ != null) hash ^= RelayRxInfo.GetHashCode(); + if (joinServerContext_ != null) hash ^= JoinServerContext.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -2061,6 +2388,10 @@ namespace Chirpstack.Integration { output.WriteRawTag(42); output.WriteMessage(RelayRxInfo); } + if (joinServerContext_ != null) { + output.WriteRawTag(50); + output.WriteMessage(JoinServerContext); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -2091,6 +2422,10 @@ namespace Chirpstack.Integration { output.WriteRawTag(42); output.WriteMessage(RelayRxInfo); } + if (joinServerContext_ != null) { + output.WriteRawTag(50); + output.WriteMessage(JoinServerContext); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -2116,6 +2451,9 @@ namespace Chirpstack.Integration { if (relayRxInfo_ != null) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(RelayRxInfo); } + if (joinServerContext_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(JoinServerContext); + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -2152,6 +2490,12 @@ namespace Chirpstack.Integration { } RelayRxInfo.MergeFrom(other.RelayRxInfo); } + if (other.joinServerContext_ != null) { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + JoinServerContext.MergeFrom(other.JoinServerContext); + } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -2196,6 +2540,13 @@ namespace Chirpstack.Integration { input.ReadMessage(RelayRxInfo); break; } + case 50: { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + input.ReadMessage(JoinServerContext); + break; + } } } #endif @@ -2240,6 +2591,13 @@ namespace Chirpstack.Integration { input.ReadMessage(RelayRxInfo); break; } + case 50: { + if (joinServerContext_ == null) { + JoinServerContext = new global::Chirpstack.Integration.JoinServerContext(); + } + input.ReadMessage(JoinServerContext); + break; + } } } } @@ -2265,7 +2623,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[4]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[5]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -2680,7 +3038,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[5]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[6]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -3142,7 +3500,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[6]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[7]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -3545,7 +3903,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[7]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[8]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -3999,7 +4357,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[8]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[9]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -4343,7 +4701,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[9]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[10]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -4766,7 +5124,7 @@ namespace Chirpstack.Integration { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[10]; } + get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[11]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] diff --git a/api/go/api/device.pb.go b/api/go/api/device.pb.go index e9d9b739..bb760fa6 100644 --- a/api/go/api/device.pb.go +++ b/api/go/api/device.pb.go @@ -1888,11 +1888,18 @@ type DeviceQueueItem struct { // object to bytes. Object *structpb.Struct `protobuf:"bytes,6,opt,name=object,proto3" json:"object,omitempty"` // Is pending. - // This is set to true when the downlink is pending. + // This is set by ChirpStack to true when the downlink is pending (e.g. it + // has been sent, but a confirmation is still pending). IsPending bool `protobuf:"varint,7,opt,name=is_pending,json=isPending,proto3" json:"is_pending,omitempty"` // Downlink frame-counter. - // This is set when the payload has been sent as downlink. + // Do not set this for plain-text data payloads. It will be automatically set + // by ChirpStack when the payload has been sent as downlink. FCntDown uint32 `protobuf:"varint,8,opt,name=f_cnt_down,json=fCntDown,proto3" json:"f_cnt_down,omitempty"` + // Is encrypted. + // This must be set to true if the end-application has already encrypted + // the data payload. In this case, the f_cnt_down field must be set to + // the corresponding frame-counter which has been used during the encryption. + IsEncrypted bool `protobuf:"varint,9,opt,name=is_encrypted,json=isEncrypted,proto3" json:"is_encrypted,omitempty"` } func (x *DeviceQueueItem) Reset() { @@ -1983,6 +1990,13 @@ func (x *DeviceQueueItem) GetFCntDown() uint32 { return 0 } +func (x *DeviceQueueItem) GetIsEncrypted() bool { + if x != nil { + return x.IsEncrypted + } + return false +} + type EnqueueDeviceQueueItemRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2288,6 +2302,102 @@ func (x *FlushDevNoncesRequest) GetDevEui() string { return "" } +type GetDeviceNextFCntDownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Device EUI (EUI64). + DevEui string `protobuf:"bytes,1,opt,name=dev_eui,json=devEui,proto3" json:"dev_eui,omitempty"` +} + +func (x *GetDeviceNextFCntDownRequest) Reset() { + *x = GetDeviceNextFCntDownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_device_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceNextFCntDownRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceNextFCntDownRequest) ProtoMessage() {} + +func (x *GetDeviceNextFCntDownRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_device_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDeviceNextFCntDownRequest.ProtoReflect.Descriptor instead. +func (*GetDeviceNextFCntDownRequest) Descriptor() ([]byte, []int) { + return file_api_device_proto_rawDescGZIP(), []int{35} +} + +func (x *GetDeviceNextFCntDownRequest) GetDevEui() string { + if x != nil { + return x.DevEui + } + return "" +} + +type GetDeviceNextFCntDownResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // FCntDown. + FCntDown uint32 `protobuf:"varint,1,opt,name=f_cnt_down,json=fCntDown,proto3" json:"f_cnt_down,omitempty"` +} + +func (x *GetDeviceNextFCntDownResponse) Reset() { + *x = GetDeviceNextFCntDownResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_device_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceNextFCntDownResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceNextFCntDownResponse) ProtoMessage() {} + +func (x *GetDeviceNextFCntDownResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_device_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDeviceNextFCntDownResponse.ProtoReflect.Descriptor instead. +func (*GetDeviceNextFCntDownResponse) Descriptor() ([]byte, []int) { + return file_api_device_proto_rawDescGZIP(), []int{36} +} + +func (x *GetDeviceNextFCntDownResponse) GetFCntDown() uint32 { + if x != nil { + return x.FCntDown + } + return 0 +} + var File_api_device_proto protoreflect.FileDescriptor var file_api_device_proto_rawDesc = []byte{ @@ -2567,7 +2677,7 @@ var file_api_device_proto_rawDesc = []byte{ 0x6b, 0x65, 0x74, 0x73, 0x50, 0x65, 0x72, 0x44, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x73, 0x22, 0xf1, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, + 0x73, 0x22, 0x94, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x12, 0x1c, @@ -2582,174 +2692,193 @@ var file_api_device_proto_rawDesc = []byte{ 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x43, 0x6e, - 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x54, 0x0a, 0x1d, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x1d, 0x45, 0x6e, 0x71, 0x75, + 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, + 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x71, 0x75, 0x65, + 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, + 0x74, 0x65, 0x6d, 0x52, 0x09, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x22, 0x30, + 0x0a, 0x1e, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x32, 0x0a, 0x17, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, + 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, + 0x76, 0x45, 0x75, 0x69, 0x22, 0x54, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x6c, 0x0a, 0x1b, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, - 0x52, 0x09, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x22, 0x30, 0x0a, 0x1e, 0x45, - 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, - 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x32, 0x0a, - 0x17, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, - 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, - 0x69, 0x22, 0x54, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, - 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x6c, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x30, 0x0a, 0x15, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, - 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x32, 0xd0, 0x10, 0x0a, 0x0d, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x06, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, - 0x22, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x54, - 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, - 0x65, 0x75, 0x69, 0x7d, 0x12, 0x64, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x30, 0x0a, 0x15, 0x46, 0x6c, 0x75, 0x73, + 0x68, 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x22, 0x37, 0x0a, 0x1c, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x46, 0x43, 0x6e, 0x74, 0x44, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, + 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, + 0x45, 0x75, 0x69, 0x22, 0x3d, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4e, 0x65, 0x78, 0x74, 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, + 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x43, 0x6e, 0x74, 0x44, 0x6f, + 0x77, 0x6e, 0x32, 0xe2, 0x11, 0x0a, 0x0d, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x18, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x1a, 0x1d, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x12, 0x5a, 0x0a, 0x06, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, - 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x12, 0x4f, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x76, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x03, 0x47, 0x65, 0x74, + 0x12, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x12, + 0x64, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2c, 0x3a, 0x01, 0x2a, 0x22, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x73, 0x2e, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, - 0x65, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x1a, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x76, + 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x12, 0x5a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, + 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, - 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x76, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2c, 0x3a, 0x01, 0x2a, 0x1a, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, - 0x2e, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x67, - 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1c, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x2a, 0x1b, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, - 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x6f, 0x0a, 0x0e, 0x46, 0x6c, 0x75, 0x73, 0x68, - 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x7d, 0x12, 0x4f, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x76, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x01, + 0x2a, 0x22, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, + 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x65, 0x0a, 0x07, 0x47, 0x65, + 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, + 0x73, 0x12, 0x76, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x12, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x01, 0x2a, + 0x1a, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x5f, + 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x67, 0x0a, 0x0a, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x2a, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x64, 0x65, - 0x76, 0x2d, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x23, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x2a, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6b, 0x65, + 0x79, 0x73, 0x12, 0x6f, 0x0a, 0x0e, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, 0x76, 0x4e, 0x6f, + 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, + 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, - 0x3a, 0x01, 0x2a, 0x22, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x6d, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x23, 0x2a, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x7d, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x23, 0x12, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, - 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x83, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, - 0x6f, 0x6d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x2a, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, + 0x2a, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, + 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x64, 0x65, 0x76, 0x2d, 0x6e, 0x6f, 0x6e, + 0x63, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x3a, 0x01, 0x2a, 0x22, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, - 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x67, 0x65, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x64, 0x6f, - 0x6d, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x12, 0x71, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x12, 0x6d, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x2a, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, - 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x82, 0x01, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, - 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x12, 0x86, 0x01, 0x0a, 0x07, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x22, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, - 0x01, 0x2a, 0x22, 0x27, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2f, 0x7b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x64, 0x65, 0x76, - 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x68, 0x0a, 0x0a, 0x46, - 0x6c, 0x75, 0x73, 0x68, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x46, 0x6c, 0x75, 0x73, 0x68, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x73, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x51, 0x75, 0x65, 0x75, - 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x7d, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x61, + 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x12, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, - 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x42, 0x63, 0x0a, 0x11, 0x69, 0x6f, - 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, - 0x0b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x69, 0xaa, 0x02, - 0x0e, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41, 0x70, 0x69, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x83, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x65, 0x76, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, + 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, + 0x6f, 0x6d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x2a, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, + 0x7d, 0x2f, 0x67, 0x65, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x64, 0x65, 0x76, + 0x2d, 0x61, 0x64, 0x64, 0x72, 0x12, 0x71, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, + 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x82, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x6e, + 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, + 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x86, 0x01, + 0x0a, 0x07, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, + 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x01, 0x2a, 0x22, 0x27, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x71, 0x75, 0x65, + 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, + 0x2f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x68, 0x0a, 0x0a, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1e, 0x2a, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x75, 0x65, + 0x12, 0x73, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x1f, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x75, + 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, + 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x7d, 0x2f, + 0x71, 0x75, 0x65, 0x75, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x78, + 0x74, 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x46, 0x43, 0x6e, + 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x65, 0x78, 0x74, + 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x01, 0x2a, 0x22, 0x2a, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x5f, 0x65, + 0x75, 0x69, 0x7d, 0x2f, 0x67, 0x65, 0x74, 0x2d, 0x6e, 0x65, 0x78, 0x74, 0x2d, 0x66, 0x2d, 0x63, + 0x6e, 0x74, 0x2d, 0x64, 0x6f, 0x77, 0x6e, 0x42, 0x63, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, + 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0b, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x69, 0xaa, 0x02, 0x0e, 0x43, 0x68, + 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2764,7 +2893,7 @@ func file_api_device_proto_rawDescGZIP() []byte { return file_api_device_proto_rawDescData } -var file_api_device_proto_msgTypes = make([]protoimpl.MessageInfo, 39) +var file_api_device_proto_msgTypes = make([]protoimpl.MessageInfo, 41) var file_api_device_proto_goTypes = []interface{}{ (*Device)(nil), // 0: api.Device (*DeviceStatus)(nil), // 1: api.DeviceStatus @@ -2801,58 +2930,60 @@ var file_api_device_proto_goTypes = []interface{}{ (*GetDeviceQueueItemsRequest)(nil), // 32: api.GetDeviceQueueItemsRequest (*GetDeviceQueueItemsResponse)(nil), // 33: api.GetDeviceQueueItemsResponse (*FlushDevNoncesRequest)(nil), // 34: api.FlushDevNoncesRequest - nil, // 35: api.Device.VariablesEntry - nil, // 36: api.Device.TagsEntry - nil, // 37: api.GetDeviceMetricsResponse.MetricsEntry - nil, // 38: api.GetDeviceMetricsResponse.StatesEntry - (*timestamppb.Timestamp)(nil), // 39: google.protobuf.Timestamp - (common.DeviceClass)(0), // 40: common.DeviceClass - (common.Aggregation)(0), // 41: common.Aggregation - (*common.Metric)(nil), // 42: common.Metric - (*structpb.Struct)(nil), // 43: google.protobuf.Struct - (*emptypb.Empty)(nil), // 44: google.protobuf.Empty + (*GetDeviceNextFCntDownRequest)(nil), // 35: api.GetDeviceNextFCntDownRequest + (*GetDeviceNextFCntDownResponse)(nil), // 36: api.GetDeviceNextFCntDownResponse + nil, // 37: api.Device.VariablesEntry + nil, // 38: api.Device.TagsEntry + nil, // 39: api.GetDeviceMetricsResponse.MetricsEntry + nil, // 40: api.GetDeviceMetricsResponse.StatesEntry + (*timestamppb.Timestamp)(nil), // 41: google.protobuf.Timestamp + (common.DeviceClass)(0), // 42: common.DeviceClass + (common.Aggregation)(0), // 43: common.Aggregation + (*common.Metric)(nil), // 44: common.Metric + (*structpb.Struct)(nil), // 45: google.protobuf.Struct + (*emptypb.Empty)(nil), // 46: google.protobuf.Empty } var file_api_device_proto_depIdxs = []int32{ - 35, // 0: api.Device.variables:type_name -> api.Device.VariablesEntry - 36, // 1: api.Device.tags:type_name -> api.Device.TagsEntry - 39, // 2: api.DeviceListItem.created_at:type_name -> google.protobuf.Timestamp - 39, // 3: api.DeviceListItem.updated_at:type_name -> google.protobuf.Timestamp - 39, // 4: api.DeviceListItem.last_seen_at:type_name -> google.protobuf.Timestamp + 37, // 0: api.Device.variables:type_name -> api.Device.VariablesEntry + 38, // 1: api.Device.tags:type_name -> api.Device.TagsEntry + 41, // 2: api.DeviceListItem.created_at:type_name -> google.protobuf.Timestamp + 41, // 3: api.DeviceListItem.updated_at:type_name -> google.protobuf.Timestamp + 41, // 4: api.DeviceListItem.last_seen_at:type_name -> google.protobuf.Timestamp 1, // 5: api.DeviceListItem.device_status:type_name -> api.DeviceStatus 0, // 6: api.CreateDeviceRequest.device:type_name -> api.Device 0, // 7: api.GetDeviceResponse.device:type_name -> api.Device - 39, // 8: api.GetDeviceResponse.created_at:type_name -> google.protobuf.Timestamp - 39, // 9: api.GetDeviceResponse.updated_at:type_name -> google.protobuf.Timestamp - 39, // 10: api.GetDeviceResponse.last_seen_at:type_name -> google.protobuf.Timestamp + 41, // 8: api.GetDeviceResponse.created_at:type_name -> google.protobuf.Timestamp + 41, // 9: api.GetDeviceResponse.updated_at:type_name -> google.protobuf.Timestamp + 41, // 10: api.GetDeviceResponse.last_seen_at:type_name -> google.protobuf.Timestamp 1, // 11: api.GetDeviceResponse.device_status:type_name -> api.DeviceStatus - 40, // 12: api.GetDeviceResponse.class_enabled:type_name -> common.DeviceClass + 42, // 12: api.GetDeviceResponse.class_enabled:type_name -> common.DeviceClass 0, // 13: api.UpdateDeviceRequest.device:type_name -> api.Device 2, // 14: api.ListDevicesResponse.result:type_name -> api.DeviceListItem 3, // 15: api.CreateDeviceKeysRequest.device_keys:type_name -> api.DeviceKeys 3, // 16: api.GetDeviceKeysResponse.device_keys:type_name -> api.DeviceKeys - 39, // 17: api.GetDeviceKeysResponse.created_at:type_name -> google.protobuf.Timestamp - 39, // 18: api.GetDeviceKeysResponse.updated_at:type_name -> google.protobuf.Timestamp + 41, // 17: api.GetDeviceKeysResponse.created_at:type_name -> google.protobuf.Timestamp + 41, // 18: api.GetDeviceKeysResponse.updated_at:type_name -> google.protobuf.Timestamp 3, // 19: api.UpdateDeviceKeysRequest.device_keys:type_name -> api.DeviceKeys 16, // 20: api.ActivateDeviceRequest.device_activation:type_name -> api.DeviceActivation 16, // 21: api.GetDeviceActivationResponse.device_activation:type_name -> api.DeviceActivation - 39, // 22: api.GetDeviceMetricsRequest.start:type_name -> google.protobuf.Timestamp - 39, // 23: api.GetDeviceMetricsRequest.end:type_name -> google.protobuf.Timestamp - 41, // 24: api.GetDeviceMetricsRequest.aggregation:type_name -> common.Aggregation - 37, // 25: api.GetDeviceMetricsResponse.metrics:type_name -> api.GetDeviceMetricsResponse.MetricsEntry - 38, // 26: api.GetDeviceMetricsResponse.states:type_name -> api.GetDeviceMetricsResponse.StatesEntry - 39, // 27: api.GetDeviceLinkMetricsRequest.start:type_name -> google.protobuf.Timestamp - 39, // 28: api.GetDeviceLinkMetricsRequest.end:type_name -> google.protobuf.Timestamp - 41, // 29: api.GetDeviceLinkMetricsRequest.aggregation:type_name -> common.Aggregation - 42, // 30: api.GetDeviceLinkMetricsResponse.rx_packets:type_name -> common.Metric - 42, // 31: api.GetDeviceLinkMetricsResponse.gw_rssi:type_name -> common.Metric - 42, // 32: api.GetDeviceLinkMetricsResponse.gw_snr:type_name -> common.Metric - 42, // 33: api.GetDeviceLinkMetricsResponse.rx_packets_per_freq:type_name -> common.Metric - 42, // 34: api.GetDeviceLinkMetricsResponse.rx_packets_per_dr:type_name -> common.Metric - 42, // 35: api.GetDeviceLinkMetricsResponse.errors:type_name -> common.Metric - 43, // 36: api.DeviceQueueItem.object:type_name -> google.protobuf.Struct + 41, // 22: api.GetDeviceMetricsRequest.start:type_name -> google.protobuf.Timestamp + 41, // 23: api.GetDeviceMetricsRequest.end:type_name -> google.protobuf.Timestamp + 43, // 24: api.GetDeviceMetricsRequest.aggregation:type_name -> common.Aggregation + 39, // 25: api.GetDeviceMetricsResponse.metrics:type_name -> api.GetDeviceMetricsResponse.MetricsEntry + 40, // 26: api.GetDeviceMetricsResponse.states:type_name -> api.GetDeviceMetricsResponse.StatesEntry + 41, // 27: api.GetDeviceLinkMetricsRequest.start:type_name -> google.protobuf.Timestamp + 41, // 28: api.GetDeviceLinkMetricsRequest.end:type_name -> google.protobuf.Timestamp + 43, // 29: api.GetDeviceLinkMetricsRequest.aggregation:type_name -> common.Aggregation + 44, // 30: api.GetDeviceLinkMetricsResponse.rx_packets:type_name -> common.Metric + 44, // 31: api.GetDeviceLinkMetricsResponse.gw_rssi:type_name -> common.Metric + 44, // 32: api.GetDeviceLinkMetricsResponse.gw_snr:type_name -> common.Metric + 44, // 33: api.GetDeviceLinkMetricsResponse.rx_packets_per_freq:type_name -> common.Metric + 44, // 34: api.GetDeviceLinkMetricsResponse.rx_packets_per_dr:type_name -> common.Metric + 44, // 35: api.GetDeviceLinkMetricsResponse.errors:type_name -> common.Metric + 45, // 36: api.DeviceQueueItem.object:type_name -> google.protobuf.Struct 28, // 37: api.EnqueueDeviceQueueItemRequest.queue_item:type_name -> api.DeviceQueueItem 28, // 38: api.GetDeviceQueueItemsResponse.result:type_name -> api.DeviceQueueItem - 42, // 39: api.GetDeviceMetricsResponse.MetricsEntry.value:type_name -> common.Metric + 44, // 39: api.GetDeviceMetricsResponse.MetricsEntry.value:type_name -> common.Metric 25, // 40: api.GetDeviceMetricsResponse.StatesEntry.value:type_name -> api.DeviceState 4, // 41: api.DeviceService.Create:input_type -> api.CreateDeviceRequest 5, // 42: api.DeviceService.Get:input_type -> api.GetDeviceRequest @@ -2873,27 +3004,29 @@ var file_api_device_proto_depIdxs = []int32{ 29, // 57: api.DeviceService.Enqueue:input_type -> api.EnqueueDeviceQueueItemRequest 31, // 58: api.DeviceService.FlushQueue:input_type -> api.FlushDeviceQueueRequest 32, // 59: api.DeviceService.GetQueue:input_type -> api.GetDeviceQueueItemsRequest - 44, // 60: api.DeviceService.Create:output_type -> google.protobuf.Empty - 6, // 61: api.DeviceService.Get:output_type -> api.GetDeviceResponse - 44, // 62: api.DeviceService.Update:output_type -> google.protobuf.Empty - 44, // 63: api.DeviceService.Delete:output_type -> google.protobuf.Empty - 10, // 64: api.DeviceService.List:output_type -> api.ListDevicesResponse - 44, // 65: api.DeviceService.CreateKeys:output_type -> google.protobuf.Empty - 13, // 66: api.DeviceService.GetKeys:output_type -> api.GetDeviceKeysResponse - 44, // 67: api.DeviceService.UpdateKeys:output_type -> google.protobuf.Empty - 44, // 68: api.DeviceService.DeleteKeys:output_type -> google.protobuf.Empty - 44, // 69: api.DeviceService.FlushDevNonces:output_type -> google.protobuf.Empty - 44, // 70: api.DeviceService.Activate:output_type -> google.protobuf.Empty - 44, // 71: api.DeviceService.Deactivate:output_type -> google.protobuf.Empty - 20, // 72: api.DeviceService.GetActivation:output_type -> api.GetDeviceActivationResponse - 22, // 73: api.DeviceService.GetRandomDevAddr:output_type -> api.GetRandomDevAddrResponse - 24, // 74: api.DeviceService.GetMetrics:output_type -> api.GetDeviceMetricsResponse - 27, // 75: api.DeviceService.GetLinkMetrics:output_type -> api.GetDeviceLinkMetricsResponse - 30, // 76: api.DeviceService.Enqueue:output_type -> api.EnqueueDeviceQueueItemResponse - 44, // 77: api.DeviceService.FlushQueue:output_type -> google.protobuf.Empty - 33, // 78: api.DeviceService.GetQueue:output_type -> api.GetDeviceQueueItemsResponse - 60, // [60:79] is the sub-list for method output_type - 41, // [41:60] is the sub-list for method input_type + 35, // 60: api.DeviceService.GetNextFCntDown:input_type -> api.GetDeviceNextFCntDownRequest + 46, // 61: api.DeviceService.Create:output_type -> google.protobuf.Empty + 6, // 62: api.DeviceService.Get:output_type -> api.GetDeviceResponse + 46, // 63: api.DeviceService.Update:output_type -> google.protobuf.Empty + 46, // 64: api.DeviceService.Delete:output_type -> google.protobuf.Empty + 10, // 65: api.DeviceService.List:output_type -> api.ListDevicesResponse + 46, // 66: api.DeviceService.CreateKeys:output_type -> google.protobuf.Empty + 13, // 67: api.DeviceService.GetKeys:output_type -> api.GetDeviceKeysResponse + 46, // 68: api.DeviceService.UpdateKeys:output_type -> google.protobuf.Empty + 46, // 69: api.DeviceService.DeleteKeys:output_type -> google.protobuf.Empty + 46, // 70: api.DeviceService.FlushDevNonces:output_type -> google.protobuf.Empty + 46, // 71: api.DeviceService.Activate:output_type -> google.protobuf.Empty + 46, // 72: api.DeviceService.Deactivate:output_type -> google.protobuf.Empty + 20, // 73: api.DeviceService.GetActivation:output_type -> api.GetDeviceActivationResponse + 22, // 74: api.DeviceService.GetRandomDevAddr:output_type -> api.GetRandomDevAddrResponse + 24, // 75: api.DeviceService.GetMetrics:output_type -> api.GetDeviceMetricsResponse + 27, // 76: api.DeviceService.GetLinkMetrics:output_type -> api.GetDeviceLinkMetricsResponse + 30, // 77: api.DeviceService.Enqueue:output_type -> api.EnqueueDeviceQueueItemResponse + 46, // 78: api.DeviceService.FlushQueue:output_type -> google.protobuf.Empty + 33, // 79: api.DeviceService.GetQueue:output_type -> api.GetDeviceQueueItemsResponse + 36, // 80: api.DeviceService.GetNextFCntDown:output_type -> api.GetDeviceNextFCntDownResponse + 61, // [61:81] is the sub-list for method output_type + 41, // [41:61] is the sub-list for method input_type 41, // [41:41] is the sub-list for extension type_name 41, // [41:41] is the sub-list for extension extendee 0, // [0:41] is the sub-list for field type_name @@ -3325,6 +3458,30 @@ func file_api_device_proto_init() { return nil } } + file_api_device_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceNextFCntDownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_device_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceNextFCntDownResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -3332,7 +3489,7 @@ func file_api_device_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_device_proto_rawDesc, NumEnums: 0, - NumMessages: 39, + NumMessages: 41, NumExtensions: 0, NumServices: 1, }, diff --git a/api/go/api/device_grpc.pb.go b/api/go/api/device_grpc.pb.go index 7d679824..5c84e0dc 100644 --- a/api/go/api/device_grpc.pb.go +++ b/api/go/api/device_grpc.pb.go @@ -39,6 +39,7 @@ const ( DeviceService_Enqueue_FullMethodName = "/api.DeviceService/Enqueue" DeviceService_FlushQueue_FullMethodName = "/api.DeviceService/FlushQueue" DeviceService_GetQueue_FullMethodName = "/api.DeviceService/GetQueue" + DeviceService_GetNextFCntDown_FullMethodName = "/api.DeviceService/GetNextFCntDown" ) // DeviceServiceClient is the client API for DeviceService service. @@ -89,6 +90,10 @@ type DeviceServiceClient interface { FlushQueue(ctx context.Context, in *FlushDeviceQueueRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // GetQueue returns the downlink device-queue. GetQueue(ctx context.Context, in *GetDeviceQueueItemsRequest, opts ...grpc.CallOption) (*GetDeviceQueueItemsResponse, error) + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + // downlinks. The difference with the DeviceActivation f_cont_down is that + // this method takes potential existing queue-items into account. + GetNextFCntDown(ctx context.Context, in *GetDeviceNextFCntDownRequest, opts ...grpc.CallOption) (*GetDeviceNextFCntDownResponse, error) } type deviceServiceClient struct { @@ -270,6 +275,15 @@ func (c *deviceServiceClient) GetQueue(ctx context.Context, in *GetDeviceQueueIt return out, nil } +func (c *deviceServiceClient) GetNextFCntDown(ctx context.Context, in *GetDeviceNextFCntDownRequest, opts ...grpc.CallOption) (*GetDeviceNextFCntDownResponse, error) { + out := new(GetDeviceNextFCntDownResponse) + err := c.cc.Invoke(ctx, DeviceService_GetNextFCntDown_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DeviceServiceServer is the server API for DeviceService service. // All implementations must embed UnimplementedDeviceServiceServer // for forward compatibility @@ -318,6 +332,10 @@ type DeviceServiceServer interface { FlushQueue(context.Context, *FlushDeviceQueueRequest) (*emptypb.Empty, error) // GetQueue returns the downlink device-queue. GetQueue(context.Context, *GetDeviceQueueItemsRequest) (*GetDeviceQueueItemsResponse, error) + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + // downlinks. The difference with the DeviceActivation f_cont_down is that + // this method takes potential existing queue-items into account. + GetNextFCntDown(context.Context, *GetDeviceNextFCntDownRequest) (*GetDeviceNextFCntDownResponse, error) mustEmbedUnimplementedDeviceServiceServer() } @@ -382,6 +400,9 @@ func (UnimplementedDeviceServiceServer) FlushQueue(context.Context, *FlushDevice func (UnimplementedDeviceServiceServer) GetQueue(context.Context, *GetDeviceQueueItemsRequest) (*GetDeviceQueueItemsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetQueue not implemented") } +func (UnimplementedDeviceServiceServer) GetNextFCntDown(context.Context, *GetDeviceNextFCntDownRequest) (*GetDeviceNextFCntDownResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNextFCntDown not implemented") +} func (UnimplementedDeviceServiceServer) mustEmbedUnimplementedDeviceServiceServer() {} // UnsafeDeviceServiceServer may be embedded to opt out of forward compatibility for this service. @@ -737,6 +758,24 @@ func _DeviceService_GetQueue_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _DeviceService_GetNextFCntDown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetDeviceNextFCntDownRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeviceServiceServer).GetNextFCntDown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeviceService_GetNextFCntDown_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeviceServiceServer).GetNextFCntDown(ctx, req.(*GetDeviceNextFCntDownRequest)) + } + return interceptor(ctx, in, info, handler) +} + // DeviceService_ServiceDesc is the grpc.ServiceDesc for DeviceService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -820,6 +859,10 @@ var DeviceService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetQueue", Handler: _DeviceService_GetQueue_Handler, }, + { + MethodName: "GetNextFCntDown", + Handler: _DeviceService_GetNextFCntDown_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/device.proto", diff --git a/api/go/integration/integration.pb.go b/api/go/integration/integration.pb.go index 6df9d4da..ba2dd08e 100644 --- a/api/go/integration/integration.pb.go +++ b/api/go/integration/integration.pb.go @@ -100,21 +100,24 @@ const ( LogCode_DOWNLINK_GATEWAY LogCode = 8 // Relay new end-device. LogCode_RELAY_NEW_END_DEVICE LogCode = 9 + // Downlink frame-counter. + LogCode_F_CNT_DOWN LogCode = 10 ) // Enum value maps for LogCode. var ( LogCode_name = map[int32]string{ - 0: "UNKNOWN", - 1: "DOWNLINK_PAYLOAD_SIZE", - 2: "UPLINK_CODEC", - 3: "DOWNLINK_CODEC", - 4: "OTAA", - 5: "UPLINK_F_CNT_RESET", - 6: "UPLINK_MIC", - 7: "UPLINK_F_CNT_RETRANSMISSION", - 8: "DOWNLINK_GATEWAY", - 9: "RELAY_NEW_END_DEVICE", + 0: "UNKNOWN", + 1: "DOWNLINK_PAYLOAD_SIZE", + 2: "UPLINK_CODEC", + 3: "DOWNLINK_CODEC", + 4: "OTAA", + 5: "UPLINK_F_CNT_RESET", + 6: "UPLINK_MIC", + 7: "UPLINK_F_CNT_RETRANSMISSION", + 8: "DOWNLINK_GATEWAY", + 9: "RELAY_NEW_END_DEVICE", + 10: "F_CNT_DOWN", } LogCode_value = map[string]int32{ "UNKNOWN": 0, @@ -127,6 +130,7 @@ var ( "UPLINK_F_CNT_RETRANSMISSION": 7, "DOWNLINK_GATEWAY": 8, "RELAY_NEW_END_DEVICE": 9, + "F_CNT_DOWN": 10, } ) @@ -381,6 +385,64 @@ func (x *UplinkRelayRxInfo) GetWorChannel() uint32 { return 0 } +// Join-Server context. +type JoinServerContext struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Session-key ID. + SessionKeyId string `protobuf:"bytes,1,opt,name=session_key_id,json=sessionKeyId,proto3" json:"session_key_id,omitempty"` + // AppSKey envelope. + AppSKey *common.KeyEnvelope `protobuf:"bytes,2,opt,name=app_s_key,json=appSKey,proto3" json:"app_s_key,omitempty"` +} + +func (x *JoinServerContext) Reset() { + *x = JoinServerContext{} + if protoimpl.UnsafeEnabled { + mi := &file_integration_integration_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JoinServerContext) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinServerContext) ProtoMessage() {} + +func (x *JoinServerContext) ProtoReflect() protoreflect.Message { + mi := &file_integration_integration_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinServerContext.ProtoReflect.Descriptor instead. +func (*JoinServerContext) Descriptor() ([]byte, []int) { + return file_integration_integration_proto_rawDescGZIP(), []int{2} +} + +func (x *JoinServerContext) GetSessionKeyId() string { + if x != nil { + return x.SessionKeyId + } + return "" +} + +func (x *JoinServerContext) GetAppSKey() *common.KeyEnvelope { + if x != nil { + return x.AppSKey + } + return nil +} + // UplinkEvent is the message sent when an uplink payload has been received. type UplinkEvent struct { state protoimpl.MessageState @@ -416,12 +478,17 @@ type UplinkEvent struct { TxInfo *gw.UplinkTxInfo `protobuf:"bytes,13,opt,name=tx_info,json=txInfo,proto3" json:"tx_info,omitempty"` // Relay info. RelayRxInfo *UplinkRelayRxInfo `protobuf:"bytes,14,opt,name=relay_rx_info,json=relayRxInfo,proto3" json:"relay_rx_info,omitempty"` + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext *JoinServerContext `protobuf:"bytes,15,opt,name=join_server_context,json=joinServerContext,proto3" json:"join_server_context,omitempty"` } func (x *UplinkEvent) Reset() { *x = UplinkEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[2] + mi := &file_integration_integration_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -434,7 +501,7 @@ func (x *UplinkEvent) String() string { func (*UplinkEvent) ProtoMessage() {} func (x *UplinkEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[2] + mi := &file_integration_integration_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -447,7 +514,7 @@ func (x *UplinkEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use UplinkEvent.ProtoReflect.Descriptor instead. func (*UplinkEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{2} + return file_integration_integration_proto_rawDescGZIP(), []int{3} } func (x *UplinkEvent) GetDeduplicationId() string { @@ -548,6 +615,13 @@ func (x *UplinkEvent) GetRelayRxInfo() *UplinkRelayRxInfo { return nil } +func (x *UplinkEvent) GetJoinServerContext() *JoinServerContext { + if x != nil { + return x.JoinServerContext + } + return nil +} + // JoinEvent is the message sent when a device joined the network. // Note: this event is sent at the first uplink after OTAA. type JoinEvent struct { @@ -565,12 +639,17 @@ type JoinEvent struct { DevAddr string `protobuf:"bytes,4,opt,name=dev_addr,json=devAddr,proto3" json:"dev_addr,omitempty"` // Relay info. RelayRxInfo *UplinkRelayRxInfo `protobuf:"bytes,5,opt,name=relay_rx_info,json=relayRxInfo,proto3" json:"relay_rx_info,omitempty"` + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext *JoinServerContext `protobuf:"bytes,6,opt,name=join_server_context,json=joinServerContext,proto3" json:"join_server_context,omitempty"` } func (x *JoinEvent) Reset() { *x = JoinEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[3] + mi := &file_integration_integration_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -583,7 +662,7 @@ func (x *JoinEvent) String() string { func (*JoinEvent) ProtoMessage() {} func (x *JoinEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[3] + mi := &file_integration_integration_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -596,7 +675,7 @@ func (x *JoinEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use JoinEvent.ProtoReflect.Descriptor instead. func (*JoinEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{3} + return file_integration_integration_proto_rawDescGZIP(), []int{4} } func (x *JoinEvent) GetDeduplicationId() string { @@ -634,6 +713,13 @@ func (x *JoinEvent) GetRelayRxInfo() *UplinkRelayRxInfo { return nil } +func (x *JoinEvent) GetJoinServerContext() *JoinServerContext { + if x != nil { + return x.JoinServerContext + } + return nil +} + // AckEvent is the message sent when a confirmation on a confirmed downlink // has been received -or- when the downlink timed out. type AckEvent struct { @@ -658,7 +744,7 @@ type AckEvent struct { func (x *AckEvent) Reset() { *x = AckEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[4] + mi := &file_integration_integration_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -671,7 +757,7 @@ func (x *AckEvent) String() string { func (*AckEvent) ProtoMessage() {} func (x *AckEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[4] + mi := &file_integration_integration_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -684,7 +770,7 @@ func (x *AckEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AckEvent.ProtoReflect.Descriptor instead. func (*AckEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{4} + return file_integration_integration_proto_rawDescGZIP(), []int{5} } func (x *AckEvent) GetDeduplicationId() string { @@ -756,7 +842,7 @@ type TxAckEvent struct { func (x *TxAckEvent) Reset() { *x = TxAckEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[5] + mi := &file_integration_integration_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -769,7 +855,7 @@ func (x *TxAckEvent) String() string { func (*TxAckEvent) ProtoMessage() {} func (x *TxAckEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[5] + mi := &file_integration_integration_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -782,7 +868,7 @@ func (x *TxAckEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use TxAckEvent.ProtoReflect.Descriptor instead. func (*TxAckEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{5} + return file_integration_integration_proto_rawDescGZIP(), []int{6} } func (x *TxAckEvent) GetDownlinkId() uint32 { @@ -857,7 +943,7 @@ type LogEvent struct { func (x *LogEvent) Reset() { *x = LogEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[6] + mi := &file_integration_integration_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -870,7 +956,7 @@ func (x *LogEvent) String() string { func (*LogEvent) ProtoMessage() {} func (x *LogEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[6] + mi := &file_integration_integration_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -883,7 +969,7 @@ func (x *LogEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use LogEvent.ProtoReflect.Descriptor instead. func (*LogEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{6} + return file_integration_integration_proto_rawDescGZIP(), []int{7} } func (x *LogEvent) GetTime() *timestamppb.Timestamp { @@ -955,7 +1041,7 @@ type StatusEvent struct { func (x *StatusEvent) Reset() { *x = StatusEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[7] + mi := &file_integration_integration_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -968,7 +1054,7 @@ func (x *StatusEvent) String() string { func (*StatusEvent) ProtoMessage() {} func (x *StatusEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[7] + mi := &file_integration_integration_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -981,7 +1067,7 @@ func (x *StatusEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusEvent.ProtoReflect.Descriptor instead. func (*StatusEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{7} + return file_integration_integration_proto_rawDescGZIP(), []int{8} } func (x *StatusEvent) GetDeduplicationId() string { @@ -1052,7 +1138,7 @@ type LocationEvent struct { func (x *LocationEvent) Reset() { *x = LocationEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[8] + mi := &file_integration_integration_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1065,7 +1151,7 @@ func (x *LocationEvent) String() string { func (*LocationEvent) ProtoMessage() {} func (x *LocationEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[8] + mi := &file_integration_integration_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1078,7 +1164,7 @@ func (x *LocationEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use LocationEvent.ProtoReflect.Descriptor instead. func (*LocationEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{8} + return file_integration_integration_proto_rawDescGZIP(), []int{9} } func (x *LocationEvent) GetDeduplicationId() string { @@ -1134,7 +1220,7 @@ type IntegrationEvent struct { func (x *IntegrationEvent) Reset() { *x = IntegrationEvent{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[9] + mi := &file_integration_integration_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1147,7 +1233,7 @@ func (x *IntegrationEvent) String() string { func (*IntegrationEvent) ProtoMessage() {} func (x *IntegrationEvent) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[9] + mi := &file_integration_integration_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1160,7 +1246,7 @@ func (x *IntegrationEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use IntegrationEvent.ProtoReflect.Descriptor instead. func (*IntegrationEvent) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{9} + return file_integration_integration_proto_rawDescGZIP(), []int{10} } func (x *IntegrationEvent) GetDeduplicationId() string { @@ -1232,7 +1318,7 @@ type DownlinkCommand struct { func (x *DownlinkCommand) Reset() { *x = DownlinkCommand{} if protoimpl.UnsafeEnabled { - mi := &file_integration_integration_proto_msgTypes[10] + mi := &file_integration_integration_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1245,7 +1331,7 @@ func (x *DownlinkCommand) String() string { func (*DownlinkCommand) ProtoMessage() {} func (x *DownlinkCommand) ProtoReflect() protoreflect.Message { - mi := &file_integration_integration_proto_msgTypes[10] + mi := &file_integration_integration_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1258,7 +1344,7 @@ func (x *DownlinkCommand) ProtoReflect() protoreflect.Message { // Deprecated: Use DownlinkCommand.ProtoReflect.Descriptor instead. func (*DownlinkCommand) Descriptor() ([]byte, []int) { - return file_integration_integration_proto_rawDescGZIP(), []int{10} + return file_integration_integration_proto_rawDescGZIP(), []int{11} } func (x *DownlinkCommand) GetId() string { @@ -1355,149 +1441,131 @@ var file_integration_integration_proto_rawDesc = []byte{ 0x01, 0x28, 0x05, 0x52, 0x03, 0x73, 0x6e, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x73, 0x73, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x72, 0x73, 0x73, 0x69, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x88, 0x04, - 0x0a, 0x0b, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, - 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x64, 0x72, 0x12, - 0x0e, 0x0a, 0x02, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x72, 0x12, - 0x13, 0x0a, 0x05, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x66, 0x43, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, - 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x29, - 0x0a, 0x07, 0x72, 0x78, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x67, 0x77, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x78, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x06, 0x72, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x07, 0x74, 0x78, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x77, 0x2e, - 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x74, 0x78, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x72, 0x78, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, - 0x52, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xff, 0x01, 0x0a, 0x09, 0x4a, 0x6f, 0x69, - 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x64, - 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, - 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x42, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, - 0x72, 0x78, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x6c, 0x69, - 0x6e, 0x6b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x72, - 0x65, 0x6c, 0x61, 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x85, 0x02, 0x0a, 0x08, 0x41, - 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, - 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, - 0x64, 0x67, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, - 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x43, 0x6e, 0x74, 0x44, 0x6f, - 0x77, 0x6e, 0x22, 0xa5, 0x02, 0x0a, 0x0a, 0x54, 0x78, 0x41, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, - 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, - 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x1d, - 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, - 0x07, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x67, 0x77, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x78, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x06, 0x74, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xe7, 0x02, 0x0a, 0x08, 0x4c, - 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x15, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, - 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x28, - 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x43, 0x6f, - 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcf, 0x02, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x72, - 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x61, 0x72, 0x67, 0x69, - 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x6f, - 0x77, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, - 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xd2, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x6a, 0x0a, + 0x11, 0x4a, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x5f, + 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, + 0x52, 0x07, 0x61, 0x70, 0x70, 0x53, 0x4b, 0x65, 0x79, 0x22, 0xd8, 0x04, 0x0a, 0x0b, 0x55, 0x70, + 0x6c, 0x69, 0x6e, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, + 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x64, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x64, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x64, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x72, 0x12, 0x13, 0x0a, 0x05, 0x66, + 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x43, 0x6e, 0x74, + 0x12, 0x15, 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x72, 0x78, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x77, + 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x72, + 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x77, 0x2e, 0x55, 0x70, 0x6c, 0x69, + 0x6e, 0x6b, 0x54, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x74, 0x78, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x42, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x72, 0x78, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x6c, 0x61, + 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x78, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4e, 0x0a, 0x13, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x4a, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x52, 0x11, 0x6a, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0xcf, 0x02, 0x0a, 0x09, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, + 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, + 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x42, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x72, 0x78, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x6c, 0x61, 0x79, 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x52, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4e, 0x0a, 0x13, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x11, 0x6a, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x85, 0x02, 0x0a, 0x08, 0x41, 0x63, 0x6b, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, + 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, + 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, + 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, + 0x61, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x61, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, + 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0xa5, + 0x02, 0x0a, 0x0a, 0x54, 0x78, 0x41, 0x63, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x2e, + 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, + 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x0a, + 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x66, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x07, 0x74, 0x78, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x77, 0x2e, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, + 0x74, 0x78, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xe7, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, - 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x02, 0x0a, 0x10, - 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, + 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x28, 0x0a, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xcf, 0x02, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, @@ -1507,51 +1575,87 @@ var file_integration_integration_proto_rawDesc = []byte{ 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x22, 0xb4, 0x01, 0x0a, 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x12, 0x1c, 0x0a, - 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x66, - 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x50, 0x6f, - 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, - 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2a, 0x2c, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x10, 0x02, 0x2a, 0xda, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x43, 0x6f, 0x64, - 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x19, - 0x0a, 0x15, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, - 0x41, 0x44, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x50, 0x4c, - 0x49, 0x4e, 0x4b, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x43, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x44, - 0x4f, 0x57, 0x4e, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x43, 0x10, 0x03, 0x12, - 0x08, 0x0a, 0x04, 0x4f, 0x54, 0x41, 0x41, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x55, 0x50, 0x4c, - 0x49, 0x4e, 0x4b, 0x5f, 0x46, 0x5f, 0x43, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, - 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4d, 0x49, 0x43, 0x10, - 0x06, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x46, 0x5f, 0x43, 0x4e, - 0x54, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, - 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x47, - 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x4c, 0x41, - 0x59, 0x5f, 0x4e, 0x45, 0x57, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, - 0x10, 0x09, 0x42, 0x81, 0x01, 0x0a, 0x1d, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x10, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x72, 0x6f, 0x63, 0x61, 0x61, 0x72, 0x2f, 0x63, 0x68, 0x69, - 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, - 0x34, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xaa, 0x02, 0x16, - 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x32, 0x0a, + 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x22, 0xd2, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x02, 0x0a, 0x10, 0x49, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, + 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xb4, 0x01, 0x0a, + 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x2a, 0x2c, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, + 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x02, 0x2a, 0xea, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x4f, + 0x57, 0x4e, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, + 0x49, 0x5a, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, + 0x43, 0x4f, 0x44, 0x45, 0x43, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x44, 0x4f, 0x57, 0x4e, 0x4c, + 0x49, 0x4e, 0x4b, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x43, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x4f, + 0x54, 0x41, 0x41, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, + 0x46, 0x5f, 0x43, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x05, 0x12, 0x0e, 0x0a, + 0x0a, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4d, 0x49, 0x43, 0x10, 0x06, 0x12, 0x1f, 0x0a, + 0x1b, 0x55, 0x50, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x46, 0x5f, 0x43, 0x4e, 0x54, 0x5f, 0x52, 0x45, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x14, + 0x0a, 0x10, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, + 0x41, 0x59, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x4c, 0x41, 0x59, 0x5f, 0x4e, 0x45, + 0x57, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x10, 0x09, 0x12, 0x0e, + 0x0a, 0x0a, 0x46, 0x5f, 0x43, 0x4e, 0x54, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x0a, 0x42, 0x81, + 0x01, 0x0a, 0x1d, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x10, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x62, 0x72, 0x6f, 0x63, 0x61, 0x61, 0x72, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xaa, 0x02, 0x16, 0x43, 0x68, 0x69, 0x72, + 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1567,67 +1671,72 @@ func file_integration_integration_proto_rawDescGZIP() []byte { } var file_integration_integration_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_integration_integration_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_integration_integration_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_integration_integration_proto_goTypes = []interface{}{ (LogLevel)(0), // 0: integration.LogLevel (LogCode)(0), // 1: integration.LogCode (*DeviceInfo)(nil), // 2: integration.DeviceInfo (*UplinkRelayRxInfo)(nil), // 3: integration.UplinkRelayRxInfo - (*UplinkEvent)(nil), // 4: integration.UplinkEvent - (*JoinEvent)(nil), // 5: integration.JoinEvent - (*AckEvent)(nil), // 6: integration.AckEvent - (*TxAckEvent)(nil), // 7: integration.TxAckEvent - (*LogEvent)(nil), // 8: integration.LogEvent - (*StatusEvent)(nil), // 9: integration.StatusEvent - (*LocationEvent)(nil), // 10: integration.LocationEvent - (*IntegrationEvent)(nil), // 11: integration.IntegrationEvent - (*DownlinkCommand)(nil), // 12: integration.DownlinkCommand - nil, // 13: integration.DeviceInfo.TagsEntry - nil, // 14: integration.LogEvent.ContextEntry - (common.DeviceClass)(0), // 15: common.DeviceClass - (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 17: google.protobuf.Struct - (*gw.UplinkRxInfo)(nil), // 18: gw.UplinkRxInfo - (*gw.UplinkTxInfo)(nil), // 19: gw.UplinkTxInfo - (*gw.DownlinkTxInfo)(nil), // 20: gw.DownlinkTxInfo - (*common.Location)(nil), // 21: common.Location + (*JoinServerContext)(nil), // 4: integration.JoinServerContext + (*UplinkEvent)(nil), // 5: integration.UplinkEvent + (*JoinEvent)(nil), // 6: integration.JoinEvent + (*AckEvent)(nil), // 7: integration.AckEvent + (*TxAckEvent)(nil), // 8: integration.TxAckEvent + (*LogEvent)(nil), // 9: integration.LogEvent + (*StatusEvent)(nil), // 10: integration.StatusEvent + (*LocationEvent)(nil), // 11: integration.LocationEvent + (*IntegrationEvent)(nil), // 12: integration.IntegrationEvent + (*DownlinkCommand)(nil), // 13: integration.DownlinkCommand + nil, // 14: integration.DeviceInfo.TagsEntry + nil, // 15: integration.LogEvent.ContextEntry + (common.DeviceClass)(0), // 16: common.DeviceClass + (*common.KeyEnvelope)(nil), // 17: common.KeyEnvelope + (*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 19: google.protobuf.Struct + (*gw.UplinkRxInfo)(nil), // 20: gw.UplinkRxInfo + (*gw.UplinkTxInfo)(nil), // 21: gw.UplinkTxInfo + (*gw.DownlinkTxInfo)(nil), // 22: gw.DownlinkTxInfo + (*common.Location)(nil), // 23: common.Location } var file_integration_integration_proto_depIdxs = []int32{ - 15, // 0: integration.DeviceInfo.device_class_enabled:type_name -> common.DeviceClass - 13, // 1: integration.DeviceInfo.tags:type_name -> integration.DeviceInfo.TagsEntry - 16, // 2: integration.UplinkEvent.time:type_name -> google.protobuf.Timestamp - 2, // 3: integration.UplinkEvent.device_info:type_name -> integration.DeviceInfo - 17, // 4: integration.UplinkEvent.object:type_name -> google.protobuf.Struct - 18, // 5: integration.UplinkEvent.rx_info:type_name -> gw.UplinkRxInfo - 19, // 6: integration.UplinkEvent.tx_info:type_name -> gw.UplinkTxInfo - 3, // 7: integration.UplinkEvent.relay_rx_info:type_name -> integration.UplinkRelayRxInfo - 16, // 8: integration.JoinEvent.time:type_name -> google.protobuf.Timestamp - 2, // 9: integration.JoinEvent.device_info:type_name -> integration.DeviceInfo - 3, // 10: integration.JoinEvent.relay_rx_info:type_name -> integration.UplinkRelayRxInfo - 16, // 11: integration.AckEvent.time:type_name -> google.protobuf.Timestamp - 2, // 12: integration.AckEvent.device_info:type_name -> integration.DeviceInfo - 16, // 13: integration.TxAckEvent.time:type_name -> google.protobuf.Timestamp - 2, // 14: integration.TxAckEvent.device_info:type_name -> integration.DeviceInfo - 20, // 15: integration.TxAckEvent.tx_info:type_name -> gw.DownlinkTxInfo - 16, // 16: integration.LogEvent.time:type_name -> google.protobuf.Timestamp - 2, // 17: integration.LogEvent.device_info:type_name -> integration.DeviceInfo - 0, // 18: integration.LogEvent.level:type_name -> integration.LogLevel - 1, // 19: integration.LogEvent.code:type_name -> integration.LogCode - 14, // 20: integration.LogEvent.context:type_name -> integration.LogEvent.ContextEntry - 16, // 21: integration.StatusEvent.time:type_name -> google.protobuf.Timestamp - 2, // 22: integration.StatusEvent.device_info:type_name -> integration.DeviceInfo - 16, // 23: integration.LocationEvent.time:type_name -> google.protobuf.Timestamp - 2, // 24: integration.LocationEvent.device_info:type_name -> integration.DeviceInfo - 21, // 25: integration.LocationEvent.location:type_name -> common.Location - 16, // 26: integration.IntegrationEvent.time:type_name -> google.protobuf.Timestamp - 2, // 27: integration.IntegrationEvent.device_info:type_name -> integration.DeviceInfo - 17, // 28: integration.IntegrationEvent.object:type_name -> google.protobuf.Struct - 17, // 29: integration.DownlinkCommand.object:type_name -> google.protobuf.Struct - 30, // [30:30] is the sub-list for method output_type - 30, // [30:30] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 16, // 0: integration.DeviceInfo.device_class_enabled:type_name -> common.DeviceClass + 14, // 1: integration.DeviceInfo.tags:type_name -> integration.DeviceInfo.TagsEntry + 17, // 2: integration.JoinServerContext.app_s_key:type_name -> common.KeyEnvelope + 18, // 3: integration.UplinkEvent.time:type_name -> google.protobuf.Timestamp + 2, // 4: integration.UplinkEvent.device_info:type_name -> integration.DeviceInfo + 19, // 5: integration.UplinkEvent.object:type_name -> google.protobuf.Struct + 20, // 6: integration.UplinkEvent.rx_info:type_name -> gw.UplinkRxInfo + 21, // 7: integration.UplinkEvent.tx_info:type_name -> gw.UplinkTxInfo + 3, // 8: integration.UplinkEvent.relay_rx_info:type_name -> integration.UplinkRelayRxInfo + 4, // 9: integration.UplinkEvent.join_server_context:type_name -> integration.JoinServerContext + 18, // 10: integration.JoinEvent.time:type_name -> google.protobuf.Timestamp + 2, // 11: integration.JoinEvent.device_info:type_name -> integration.DeviceInfo + 3, // 12: integration.JoinEvent.relay_rx_info:type_name -> integration.UplinkRelayRxInfo + 4, // 13: integration.JoinEvent.join_server_context:type_name -> integration.JoinServerContext + 18, // 14: integration.AckEvent.time:type_name -> google.protobuf.Timestamp + 2, // 15: integration.AckEvent.device_info:type_name -> integration.DeviceInfo + 18, // 16: integration.TxAckEvent.time:type_name -> google.protobuf.Timestamp + 2, // 17: integration.TxAckEvent.device_info:type_name -> integration.DeviceInfo + 22, // 18: integration.TxAckEvent.tx_info:type_name -> gw.DownlinkTxInfo + 18, // 19: integration.LogEvent.time:type_name -> google.protobuf.Timestamp + 2, // 20: integration.LogEvent.device_info:type_name -> integration.DeviceInfo + 0, // 21: integration.LogEvent.level:type_name -> integration.LogLevel + 1, // 22: integration.LogEvent.code:type_name -> integration.LogCode + 15, // 23: integration.LogEvent.context:type_name -> integration.LogEvent.ContextEntry + 18, // 24: integration.StatusEvent.time:type_name -> google.protobuf.Timestamp + 2, // 25: integration.StatusEvent.device_info:type_name -> integration.DeviceInfo + 18, // 26: integration.LocationEvent.time:type_name -> google.protobuf.Timestamp + 2, // 27: integration.LocationEvent.device_info:type_name -> integration.DeviceInfo + 23, // 28: integration.LocationEvent.location:type_name -> common.Location + 18, // 29: integration.IntegrationEvent.time:type_name -> google.protobuf.Timestamp + 2, // 30: integration.IntegrationEvent.device_info:type_name -> integration.DeviceInfo + 19, // 31: integration.IntegrationEvent.object:type_name -> google.protobuf.Struct + 19, // 32: integration.DownlinkCommand.object:type_name -> google.protobuf.Struct + 33, // [33:33] is the sub-list for method output_type + 33, // [33:33] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_integration_integration_proto_init() } @@ -1661,7 +1770,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UplinkEvent); i { + switch v := v.(*JoinServerContext); i { case 0: return &v.state case 1: @@ -1673,7 +1782,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JoinEvent); i { + switch v := v.(*UplinkEvent); i { case 0: return &v.state case 1: @@ -1685,7 +1794,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AckEvent); i { + switch v := v.(*JoinEvent); i { case 0: return &v.state case 1: @@ -1697,7 +1806,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TxAckEvent); i { + switch v := v.(*AckEvent); i { case 0: return &v.state case 1: @@ -1709,7 +1818,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogEvent); i { + switch v := v.(*TxAckEvent); i { case 0: return &v.state case 1: @@ -1721,7 +1830,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusEvent); i { + switch v := v.(*LogEvent); i { case 0: return &v.state case 1: @@ -1733,7 +1842,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LocationEvent); i { + switch v := v.(*StatusEvent); i { case 0: return &v.state case 1: @@ -1745,7 +1854,7 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IntegrationEvent); i { + switch v := v.(*LocationEvent); i { case 0: return &v.state case 1: @@ -1757,6 +1866,18 @@ func file_integration_integration_proto_init() { } } file_integration_integration_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntegrationEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_integration_integration_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DownlinkCommand); i { case 0: return &v.state @@ -1775,7 +1896,7 @@ func file_integration_integration_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_integration_integration_proto_rawDesc, NumEnums: 2, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/api/grpc-web/api/device_grpc_web_pb.d.ts b/api/grpc-web/api/device_grpc_web_pb.d.ts index 3f2cefde..b86739f7 100644 --- a/api/grpc-web/api/device_grpc_web_pb.d.ts +++ b/api/grpc-web/api/device_grpc_web_pb.d.ts @@ -142,6 +142,13 @@ export class DeviceServiceClient { response: api_device_pb.GetDeviceQueueItemsResponse) => void ): grpcWeb.ClientReadableStream; + getNextFCntDown( + request: api_device_pb.GetDeviceNextFCntDownRequest, + metadata: grpcWeb.Metadata | undefined, + callback: (err: grpcWeb.RpcError, + response: api_device_pb.GetDeviceNextFCntDownResponse) => void + ): grpcWeb.ClientReadableStream; + } export class DeviceServicePromiseClient { @@ -244,5 +251,10 @@ export class DeviceServicePromiseClient { metadata?: grpcWeb.Metadata ): Promise; + getNextFCntDown( + request: api_device_pb.GetDeviceNextFCntDownRequest, + metadata?: grpcWeb.Metadata + ): Promise; + } diff --git a/api/grpc-web/api/device_grpc_web_pb.js b/api/grpc-web/api/device_grpc_web_pb.js index 13cb3ed0..7d21c181 100644 --- a/api/grpc-web/api/device_grpc_web_pb.js +++ b/api/grpc-web/api/device_grpc_web_pb.js @@ -1243,5 +1243,66 @@ proto.api.DeviceServicePromiseClient.prototype.getQueue = }; +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.api.GetDeviceNextFCntDownRequest, + * !proto.api.GetDeviceNextFCntDownResponse>} + */ +const methodDescriptor_DeviceService_GetNextFCntDown = new grpc.web.MethodDescriptor( + '/api.DeviceService/GetNextFCntDown', + grpc.web.MethodType.UNARY, + proto.api.GetDeviceNextFCntDownRequest, + proto.api.GetDeviceNextFCntDownResponse, + /** + * @param {!proto.api.GetDeviceNextFCntDownRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.api.GetDeviceNextFCntDownResponse.deserializeBinary +); + + +/** + * @param {!proto.api.GetDeviceNextFCntDownRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.api.GetDeviceNextFCntDownResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.api.DeviceServiceClient.prototype.getNextFCntDown = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/api.DeviceService/GetNextFCntDown', + request, + metadata || {}, + methodDescriptor_DeviceService_GetNextFCntDown, + callback); +}; + + +/** + * @param {!proto.api.GetDeviceNextFCntDownRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.api.DeviceServicePromiseClient.prototype.getNextFCntDown = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/api.DeviceService/GetNextFCntDown', + request, + metadata || {}, + methodDescriptor_DeviceService_GetNextFCntDown); +}; + + module.exports = proto.api; diff --git a/api/grpc-web/api/device_pb.d.ts b/api/grpc-web/api/device_pb.d.ts index 81ba841a..a6b0fbc2 100644 --- a/api/grpc-web/api/device_pb.d.ts +++ b/api/grpc-web/api/device_pb.d.ts @@ -814,6 +814,9 @@ export class DeviceQueueItem extends jspb.Message { getFCntDown(): number; setFCntDown(value: number): DeviceQueueItem; + getIsEncrypted(): boolean; + setIsEncrypted(value: boolean): DeviceQueueItem; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): DeviceQueueItem.AsObject; static toObject(includeInstance: boolean, msg: DeviceQueueItem): DeviceQueueItem.AsObject; @@ -832,6 +835,7 @@ export namespace DeviceQueueItem { object?: google_protobuf_struct_pb.Struct.AsObject, isPending: boolean, fCntDown: number, + isEncrypted: boolean, } } @@ -955,3 +959,39 @@ export namespace FlushDevNoncesRequest { } } +export class GetDeviceNextFCntDownRequest extends jspb.Message { + getDevEui(): string; + setDevEui(value: string): GetDeviceNextFCntDownRequest; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetDeviceNextFCntDownRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetDeviceNextFCntDownRequest): GetDeviceNextFCntDownRequest.AsObject; + static serializeBinaryToWriter(message: GetDeviceNextFCntDownRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetDeviceNextFCntDownRequest; + static deserializeBinaryFromReader(message: GetDeviceNextFCntDownRequest, reader: jspb.BinaryReader): GetDeviceNextFCntDownRequest; +} + +export namespace GetDeviceNextFCntDownRequest { + export type AsObject = { + devEui: string, + } +} + +export class GetDeviceNextFCntDownResponse extends jspb.Message { + getFCntDown(): number; + setFCntDown(value: number): GetDeviceNextFCntDownResponse; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetDeviceNextFCntDownResponse.AsObject; + static toObject(includeInstance: boolean, msg: GetDeviceNextFCntDownResponse): GetDeviceNextFCntDownResponse.AsObject; + static serializeBinaryToWriter(message: GetDeviceNextFCntDownResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetDeviceNextFCntDownResponse; + static deserializeBinaryFromReader(message: GetDeviceNextFCntDownResponse, reader: jspb.BinaryReader): GetDeviceNextFCntDownResponse; +} + +export namespace GetDeviceNextFCntDownResponse { + export type AsObject = { + fCntDown: number, + } +} + diff --git a/api/grpc-web/api/device_pb.js b/api/grpc-web/api/device_pb.js index 8c20a215..962d527a 100644 --- a/api/grpc-web/api/device_pb.js +++ b/api/grpc-web/api/device_pb.js @@ -47,6 +47,8 @@ goog.exportSymbol('proto.api.GetDeviceLinkMetricsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceLinkMetricsResponse', null, global); goog.exportSymbol('proto.api.GetDeviceMetricsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceMetricsResponse', null, global); +goog.exportSymbol('proto.api.GetDeviceNextFCntDownRequest', null, global); +goog.exportSymbol('proto.api.GetDeviceNextFCntDownResponse', null, global); goog.exportSymbol('proto.api.GetDeviceQueueItemsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceQueueItemsResponse', null, global); goog.exportSymbol('proto.api.GetDeviceRequest', null, global); @@ -792,6 +794,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.api.FlushDevNoncesRequest.displayName = 'proto.api.FlushDevNoncesRequest'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.api.GetDeviceNextFCntDownRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.api.GetDeviceNextFCntDownRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.api.GetDeviceNextFCntDownRequest.displayName = 'proto.api.GetDeviceNextFCntDownRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.api.GetDeviceNextFCntDownResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.api.GetDeviceNextFCntDownResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.api.GetDeviceNextFCntDownResponse.displayName = 'proto.api.GetDeviceNextFCntDownResponse'; +} @@ -6721,7 +6765,8 @@ proto.api.DeviceQueueItem.toObject = function(includeInstance, msg) { data: msg.getData_asB64(), object: (f = msg.getObject()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f), isPending: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - fCntDown: jspb.Message.getFieldWithDefault(msg, 8, 0) + fCntDown: jspb.Message.getFieldWithDefault(msg, 8, 0), + isEncrypted: jspb.Message.getBooleanFieldWithDefault(msg, 9, false) }; if (includeInstance) { @@ -6791,6 +6836,10 @@ proto.api.DeviceQueueItem.deserializeBinaryFromReader = function(msg, reader) { var value = /** @type {number} */ (reader.readUint32()); msg.setFCntDown(value); break; + case 9: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsEncrypted(value); + break; default: reader.skipField(); break; @@ -6877,6 +6926,13 @@ proto.api.DeviceQueueItem.serializeBinaryToWriter = function(message, writer) { f ); } + f = message.getIsEncrypted(); + if (f) { + writer.writeBool( + 9, + f + ); + } }; @@ -7067,6 +7123,24 @@ proto.api.DeviceQueueItem.prototype.setFCntDown = function(value) { }; +/** + * optional bool is_encrypted = 9; + * @return {boolean} + */ +proto.api.DeviceQueueItem.prototype.getIsEncrypted = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.DeviceQueueItem} returns this + */ +proto.api.DeviceQueueItem.prototype.setIsEncrypted = function(value) { + return jspb.Message.setProto3BooleanField(this, 9, value); +}; + + @@ -7958,4 +8032,264 @@ proto.api.FlushDevNoncesRequest.prototype.setDevEui = function(value) { }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.toObject = function(opt_includeInstance) { + return proto.api.GetDeviceNextFCntDownRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.api.GetDeviceNextFCntDownRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownRequest.toObject = function(includeInstance, msg) { + var f, obj = { + devEui: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.api.GetDeviceNextFCntDownRequest} + */ +proto.api.GetDeviceNextFCntDownRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.api.GetDeviceNextFCntDownRequest; + return proto.api.GetDeviceNextFCntDownRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.api.GetDeviceNextFCntDownRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.api.GetDeviceNextFCntDownRequest} + */ +proto.api.GetDeviceNextFCntDownRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setDevEui(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.api.GetDeviceNextFCntDownRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.api.GetDeviceNextFCntDownRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDevEui(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string dev_eui = 1; + * @return {string} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.getDevEui = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.api.GetDeviceNextFCntDownRequest} returns this + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.setDevEui = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.toObject = function(opt_includeInstance) { + return proto.api.GetDeviceNextFCntDownResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.api.GetDeviceNextFCntDownResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownResponse.toObject = function(includeInstance, msg) { + var f, obj = { + fCntDown: jspb.Message.getFieldWithDefault(msg, 1, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.api.GetDeviceNextFCntDownResponse} + */ +proto.api.GetDeviceNextFCntDownResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.api.GetDeviceNextFCntDownResponse; + return proto.api.GetDeviceNextFCntDownResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.api.GetDeviceNextFCntDownResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.api.GetDeviceNextFCntDownResponse} + */ +proto.api.GetDeviceNextFCntDownResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFCntDown(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.api.GetDeviceNextFCntDownResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.api.GetDeviceNextFCntDownResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getFCntDown(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } +}; + + +/** + * optional uint32 f_cnt_down = 1; + * @return {number} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.getFCntDown = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.api.GetDeviceNextFCntDownResponse} returns this + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.setFCntDown = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + goog.object.extend(exports, proto.api); diff --git a/api/js/api/device_grpc_pb.d.ts b/api/js/api/device_grpc_pb.d.ts index fe4e0082..9c2bf103 100644 --- a/api/js/api/device_grpc_pb.d.ts +++ b/api/js/api/device_grpc_pb.d.ts @@ -27,6 +27,7 @@ interface IDeviceServiceService extends grpc.ServiceDefinition; flushQueue: grpc.MethodDefinition; getQueue: grpc.MethodDefinition; + getNextFCntDown: grpc.MethodDefinition; } export const DeviceServiceService: IDeviceServiceService; @@ -51,6 +52,7 @@ export interface IDeviceServiceServer extends grpc.UntypedServiceImplementation enqueue: grpc.handleUnaryCall; flushQueue: grpc.handleUnaryCall; getQueue: grpc.handleUnaryCall; + getNextFCntDown: grpc.handleUnaryCall; } export class DeviceServiceClient extends grpc.Client { @@ -112,4 +114,7 @@ export class DeviceServiceClient extends grpc.Client { getQueue(argument: api_device_pb.GetDeviceQueueItemsRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; getQueue(argument: api_device_pb.GetDeviceQueueItemsRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; getQueue(argument: api_device_pb.GetDeviceQueueItemsRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getNextFCntDown(argument: api_device_pb.GetDeviceNextFCntDownRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getNextFCntDown(argument: api_device_pb.GetDeviceNextFCntDownRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getNextFCntDown(argument: api_device_pb.GetDeviceNextFCntDownRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; } diff --git a/api/js/api/device_grpc_pb.js b/api/js/api/device_grpc_pb.js index 579a098a..3b8db85f 100644 --- a/api/js/api/device_grpc_pb.js +++ b/api/js/api/device_grpc_pb.js @@ -207,6 +207,28 @@ function deserialize_api_GetDeviceMetricsResponse(buffer_arg) { return api_device_pb.GetDeviceMetricsResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_api_GetDeviceNextFCntDownRequest(arg) { + if (!(arg instanceof api_device_pb.GetDeviceNextFCntDownRequest)) { + throw new Error('Expected argument of type api.GetDeviceNextFCntDownRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_api_GetDeviceNextFCntDownRequest(buffer_arg) { + return api_device_pb.GetDeviceNextFCntDownRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_api_GetDeviceNextFCntDownResponse(arg) { + if (!(arg instanceof api_device_pb.GetDeviceNextFCntDownResponse)) { + throw new Error('Expected argument of type api.GetDeviceNextFCntDownResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_api_GetDeviceNextFCntDownResponse(buffer_arg) { + return api_device_pb.GetDeviceNextFCntDownResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_api_GetDeviceQueueItemsRequest(arg) { if (!(arg instanceof api_device_pb.GetDeviceQueueItemsRequest)) { throw new Error('Expected argument of type api.GetDeviceQueueItemsRequest'); @@ -565,6 +587,20 @@ getQueue: { responseSerialize: serialize_api_GetDeviceQueueItemsResponse, responseDeserialize: deserialize_api_GetDeviceQueueItemsResponse, }, + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted +// downlinks. The difference with the DeviceActivation f_cont_down is that +// this method takes potential existing queue-items into account. +getNextFCntDown: { + path: '/api.DeviceService/GetNextFCntDown', + requestStream: false, + responseStream: false, + requestType: api_device_pb.GetDeviceNextFCntDownRequest, + responseType: api_device_pb.GetDeviceNextFCntDownResponse, + requestSerialize: serialize_api_GetDeviceNextFCntDownRequest, + requestDeserialize: deserialize_api_GetDeviceNextFCntDownRequest, + responseSerialize: serialize_api_GetDeviceNextFCntDownResponse, + responseDeserialize: deserialize_api_GetDeviceNextFCntDownResponse, + }, }; exports.DeviceServiceClient = grpc.makeGenericClientConstructor(DeviceServiceService); diff --git a/api/js/api/device_pb.d.ts b/api/js/api/device_pb.d.ts index f90cd36d..a7d9cbb3 100644 --- a/api/js/api/device_pb.d.ts +++ b/api/js/api/device_pb.d.ts @@ -867,6 +867,9 @@ export class DeviceQueueItem extends jspb.Message { getFCntDown(): number; setFCntDown(value: number): void; + getIsEncrypted(): boolean; + setIsEncrypted(value: boolean): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): DeviceQueueItem.AsObject; static toObject(includeInstance: boolean, msg: DeviceQueueItem): DeviceQueueItem.AsObject; @@ -887,6 +890,7 @@ export namespace DeviceQueueItem { object?: google_protobuf_struct_pb.Struct.AsObject, isPending: boolean, fCntDown: number, + isEncrypted: boolean, } } @@ -1022,3 +1026,43 @@ export namespace FlushDevNoncesRequest { } } +export class GetDeviceNextFCntDownRequest extends jspb.Message { + getDevEui(): string; + setDevEui(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetDeviceNextFCntDownRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetDeviceNextFCntDownRequest): GetDeviceNextFCntDownRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetDeviceNextFCntDownRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetDeviceNextFCntDownRequest; + static deserializeBinaryFromReader(message: GetDeviceNextFCntDownRequest, reader: jspb.BinaryReader): GetDeviceNextFCntDownRequest; +} + +export namespace GetDeviceNextFCntDownRequest { + export type AsObject = { + devEui: string, + } +} + +export class GetDeviceNextFCntDownResponse extends jspb.Message { + getFCntDown(): number; + setFCntDown(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetDeviceNextFCntDownResponse.AsObject; + static toObject(includeInstance: boolean, msg: GetDeviceNextFCntDownResponse): GetDeviceNextFCntDownResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetDeviceNextFCntDownResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetDeviceNextFCntDownResponse; + static deserializeBinaryFromReader(message: GetDeviceNextFCntDownResponse, reader: jspb.BinaryReader): GetDeviceNextFCntDownResponse; +} + +export namespace GetDeviceNextFCntDownResponse { + export type AsObject = { + fCntDown: number, + } +} + diff --git a/api/js/api/device_pb.js b/api/js/api/device_pb.js index 1cc562ca..c87438bd 100644 --- a/api/js/api/device_pb.js +++ b/api/js/api/device_pb.js @@ -47,6 +47,8 @@ goog.exportSymbol('proto.api.GetDeviceLinkMetricsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceLinkMetricsResponse', null, global); goog.exportSymbol('proto.api.GetDeviceMetricsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceMetricsResponse', null, global); +goog.exportSymbol('proto.api.GetDeviceNextFCntDownRequest', null, global); +goog.exportSymbol('proto.api.GetDeviceNextFCntDownResponse', null, global); goog.exportSymbol('proto.api.GetDeviceQueueItemsRequest', null, global); goog.exportSymbol('proto.api.GetDeviceQueueItemsResponse', null, global); goog.exportSymbol('proto.api.GetDeviceRequest', null, global); @@ -792,6 +794,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.api.FlushDevNoncesRequest.displayName = 'proto.api.FlushDevNoncesRequest'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.api.GetDeviceNextFCntDownRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.api.GetDeviceNextFCntDownRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.api.GetDeviceNextFCntDownRequest.displayName = 'proto.api.GetDeviceNextFCntDownRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.api.GetDeviceNextFCntDownResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.api.GetDeviceNextFCntDownResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.api.GetDeviceNextFCntDownResponse.displayName = 'proto.api.GetDeviceNextFCntDownResponse'; +} @@ -6721,7 +6765,8 @@ proto.api.DeviceQueueItem.toObject = function(includeInstance, msg) { data: msg.getData_asB64(), object: (f = msg.getObject()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f), isPending: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - fCntDown: jspb.Message.getFieldWithDefault(msg, 8, 0) + fCntDown: jspb.Message.getFieldWithDefault(msg, 8, 0), + isEncrypted: jspb.Message.getBooleanFieldWithDefault(msg, 9, false) }; if (includeInstance) { @@ -6791,6 +6836,10 @@ proto.api.DeviceQueueItem.deserializeBinaryFromReader = function(msg, reader) { var value = /** @type {number} */ (reader.readUint32()); msg.setFCntDown(value); break; + case 9: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsEncrypted(value); + break; default: reader.skipField(); break; @@ -6877,6 +6926,13 @@ proto.api.DeviceQueueItem.serializeBinaryToWriter = function(message, writer) { f ); } + f = message.getIsEncrypted(); + if (f) { + writer.writeBool( + 9, + f + ); + } }; @@ -7067,6 +7123,24 @@ proto.api.DeviceQueueItem.prototype.setFCntDown = function(value) { }; +/** + * optional bool is_encrypted = 9; + * @return {boolean} + */ +proto.api.DeviceQueueItem.prototype.getIsEncrypted = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.DeviceQueueItem} returns this + */ +proto.api.DeviceQueueItem.prototype.setIsEncrypted = function(value) { + return jspb.Message.setProto3BooleanField(this, 9, value); +}; + + @@ -7958,4 +8032,264 @@ proto.api.FlushDevNoncesRequest.prototype.setDevEui = function(value) { }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.toObject = function(opt_includeInstance) { + return proto.api.GetDeviceNextFCntDownRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.api.GetDeviceNextFCntDownRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownRequest.toObject = function(includeInstance, msg) { + var f, obj = { + devEui: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.api.GetDeviceNextFCntDownRequest} + */ +proto.api.GetDeviceNextFCntDownRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.api.GetDeviceNextFCntDownRequest; + return proto.api.GetDeviceNextFCntDownRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.api.GetDeviceNextFCntDownRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.api.GetDeviceNextFCntDownRequest} + */ +proto.api.GetDeviceNextFCntDownRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setDevEui(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.api.GetDeviceNextFCntDownRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.api.GetDeviceNextFCntDownRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDevEui(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string dev_eui = 1; + * @return {string} + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.getDevEui = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.api.GetDeviceNextFCntDownRequest} returns this + */ +proto.api.GetDeviceNextFCntDownRequest.prototype.setDevEui = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.toObject = function(opt_includeInstance) { + return proto.api.GetDeviceNextFCntDownResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.api.GetDeviceNextFCntDownResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownResponse.toObject = function(includeInstance, msg) { + var f, obj = { + fCntDown: jspb.Message.getFieldWithDefault(msg, 1, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.api.GetDeviceNextFCntDownResponse} + */ +proto.api.GetDeviceNextFCntDownResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.api.GetDeviceNextFCntDownResponse; + return proto.api.GetDeviceNextFCntDownResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.api.GetDeviceNextFCntDownResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.api.GetDeviceNextFCntDownResponse} + */ +proto.api.GetDeviceNextFCntDownResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFCntDown(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.api.GetDeviceNextFCntDownResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.api.GetDeviceNextFCntDownResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.api.GetDeviceNextFCntDownResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getFCntDown(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } +}; + + +/** + * optional uint32 f_cnt_down = 1; + * @return {number} + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.getFCntDown = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.api.GetDeviceNextFCntDownResponse} returns this + */ +proto.api.GetDeviceNextFCntDownResponse.prototype.setFCntDown = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + goog.object.extend(exports, proto.api); diff --git a/api/js/integration/integration_pb.d.ts b/api/js/integration/integration_pb.d.ts index 768dfc6a..7f20f849 100644 --- a/api/js/integration/integration_pb.d.ts +++ b/api/js/integration/integration_pb.d.ts @@ -102,6 +102,32 @@ export namespace UplinkRelayRxInfo { } } +export class JoinServerContext extends jspb.Message { + getSessionKeyId(): string; + setSessionKeyId(value: string): void; + + hasAppSKey(): boolean; + clearAppSKey(): void; + getAppSKey(): common_common_pb.KeyEnvelope | undefined; + setAppSKey(value?: common_common_pb.KeyEnvelope): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): JoinServerContext.AsObject; + static toObject(includeInstance: boolean, msg: JoinServerContext): JoinServerContext.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: JoinServerContext, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): JoinServerContext; + static deserializeBinaryFromReader(message: JoinServerContext, reader: jspb.BinaryReader): JoinServerContext; +} + +export namespace JoinServerContext { + export type AsObject = { + sessionKeyId: string, + appSKey?: common_common_pb.KeyEnvelope.AsObject, + } +} + export class UplinkEvent extends jspb.Message { getDeduplicationId(): string; setDeduplicationId(value: string): void; @@ -159,6 +185,11 @@ export class UplinkEvent extends jspb.Message { getRelayRxInfo(): UplinkRelayRxInfo | undefined; setRelayRxInfo(value?: UplinkRelayRxInfo): void; + hasJoinServerContext(): boolean; + clearJoinServerContext(): void; + getJoinServerContext(): JoinServerContext | undefined; + setJoinServerContext(value?: JoinServerContext): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): UplinkEvent.AsObject; static toObject(includeInstance: boolean, msg: UplinkEvent): UplinkEvent.AsObject; @@ -185,6 +216,7 @@ export namespace UplinkEvent { rxInfoList: Array, txInfo?: gw_gw_pb.UplinkTxInfo.AsObject, relayRxInfo?: UplinkRelayRxInfo.AsObject, + joinServerContext?: JoinServerContext.AsObject, } } @@ -210,6 +242,11 @@ export class JoinEvent extends jspb.Message { getRelayRxInfo(): UplinkRelayRxInfo | undefined; setRelayRxInfo(value?: UplinkRelayRxInfo): void; + hasJoinServerContext(): boolean; + clearJoinServerContext(): void; + getJoinServerContext(): JoinServerContext | undefined; + setJoinServerContext(value?: JoinServerContext): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): JoinEvent.AsObject; static toObject(includeInstance: boolean, msg: JoinEvent): JoinEvent.AsObject; @@ -227,6 +264,7 @@ export namespace JoinEvent { deviceInfo?: DeviceInfo.AsObject, devAddr: string, relayRxInfo?: UplinkRelayRxInfo.AsObject, + joinServerContext?: JoinServerContext.AsObject, } } @@ -562,6 +600,7 @@ export interface LogCodeMap { UPLINK_F_CNT_RETRANSMISSION: 7; DOWNLINK_GATEWAY: 8; RELAY_NEW_END_DEVICE: 9; + F_CNT_DOWN: 10; } export const LogCode: LogCodeMap; diff --git a/api/js/integration/integration_pb.js b/api/js/integration/integration_pb.js index 0cbc7b29..099e1fd1 100644 --- a/api/js/integration/integration_pb.js +++ b/api/js/integration/integration_pb.js @@ -25,6 +25,7 @@ goog.exportSymbol('proto.integration.DeviceInfo', null, global); goog.exportSymbol('proto.integration.DownlinkCommand', null, global); goog.exportSymbol('proto.integration.IntegrationEvent', null, global); goog.exportSymbol('proto.integration.JoinEvent', null, global); +goog.exportSymbol('proto.integration.JoinServerContext', null, global); goog.exportSymbol('proto.integration.LocationEvent', null, global); goog.exportSymbol('proto.integration.LogCode', null, global); goog.exportSymbol('proto.integration.LogEvent', null, global); @@ -75,6 +76,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.integration.UplinkRelayRxInfo.displayName = 'proto.integration.UplinkRelayRxInfo'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.integration.JoinServerContext = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.integration.JoinServerContext, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.integration.JoinServerContext.displayName = 'proto.integration.JoinServerContext'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -948,6 +970,187 @@ proto.integration.UplinkRelayRxInfo.prototype.setWorChannel = function(value) { + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.integration.JoinServerContext.prototype.toObject = function(opt_includeInstance) { + return proto.integration.JoinServerContext.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.integration.JoinServerContext} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.integration.JoinServerContext.toObject = function(includeInstance, msg) { + var f, obj = { + sessionKeyId: jspb.Message.getFieldWithDefault(msg, 1, ""), + appSKey: (f = msg.getAppSKey()) && common_common_pb.KeyEnvelope.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.integration.JoinServerContext} + */ +proto.integration.JoinServerContext.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.integration.JoinServerContext; + return proto.integration.JoinServerContext.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.integration.JoinServerContext} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.integration.JoinServerContext} + */ +proto.integration.JoinServerContext.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setSessionKeyId(value); + break; + case 2: + var value = new common_common_pb.KeyEnvelope; + reader.readMessage(value,common_common_pb.KeyEnvelope.deserializeBinaryFromReader); + msg.setAppSKey(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.integration.JoinServerContext.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.integration.JoinServerContext.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.integration.JoinServerContext} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.integration.JoinServerContext.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getSessionKeyId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getAppSKey(); + if (f != null) { + writer.writeMessage( + 2, + f, + common_common_pb.KeyEnvelope.serializeBinaryToWriter + ); + } +}; + + +/** + * optional string session_key_id = 1; + * @return {string} + */ +proto.integration.JoinServerContext.prototype.getSessionKeyId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.integration.JoinServerContext} returns this + */ +proto.integration.JoinServerContext.prototype.setSessionKeyId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional common.KeyEnvelope app_s_key = 2; + * @return {?proto.common.KeyEnvelope} + */ +proto.integration.JoinServerContext.prototype.getAppSKey = function() { + return /** @type{?proto.common.KeyEnvelope} */ ( + jspb.Message.getWrapperField(this, common_common_pb.KeyEnvelope, 2)); +}; + + +/** + * @param {?proto.common.KeyEnvelope|undefined} value + * @return {!proto.integration.JoinServerContext} returns this +*/ +proto.integration.JoinServerContext.prototype.setAppSKey = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.integration.JoinServerContext} returns this + */ +proto.integration.JoinServerContext.prototype.clearAppSKey = function() { + return this.setAppSKey(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.integration.JoinServerContext.prototype.hasAppSKey = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + /** * List of repeated fields within this message type. * @private {!Array} @@ -1000,7 +1203,8 @@ proto.integration.UplinkEvent.toObject = function(includeInstance, msg) { rxInfoList: jspb.Message.toObjectList(msg.getRxInfoList(), gw_gw_pb.UplinkRxInfo.toObject, includeInstance), txInfo: (f = msg.getTxInfo()) && gw_gw_pb.UplinkTxInfo.toObject(includeInstance, f), - relayRxInfo: (f = msg.getRelayRxInfo()) && proto.integration.UplinkRelayRxInfo.toObject(includeInstance, f) + relayRxInfo: (f = msg.getRelayRxInfo()) && proto.integration.UplinkRelayRxInfo.toObject(includeInstance, f), + joinServerContext: (f = msg.getJoinServerContext()) && proto.integration.JoinServerContext.toObject(includeInstance, f) }; if (includeInstance) { @@ -1099,6 +1303,11 @@ proto.integration.UplinkEvent.deserializeBinaryFromReader = function(msg, reader reader.readMessage(value,proto.integration.UplinkRelayRxInfo.deserializeBinaryFromReader); msg.setRelayRxInfo(value); break; + case 15: + var value = new proto.integration.JoinServerContext; + reader.readMessage(value,proto.integration.JoinServerContext.deserializeBinaryFromReader); + msg.setJoinServerContext(value); + break; default: reader.skipField(); break; @@ -1232,6 +1441,14 @@ proto.integration.UplinkEvent.serializeBinaryToWriter = function(message, writer proto.integration.UplinkRelayRxInfo.serializeBinaryToWriter ); } + f = message.getJoinServerContext(); + if (f != null) { + writer.writeMessage( + 15, + f, + proto.integration.JoinServerContext.serializeBinaryToWriter + ); + } }; @@ -1626,6 +1843,43 @@ proto.integration.UplinkEvent.prototype.hasRelayRxInfo = function() { }; +/** + * optional JoinServerContext join_server_context = 15; + * @return {?proto.integration.JoinServerContext} + */ +proto.integration.UplinkEvent.prototype.getJoinServerContext = function() { + return /** @type{?proto.integration.JoinServerContext} */ ( + jspb.Message.getWrapperField(this, proto.integration.JoinServerContext, 15)); +}; + + +/** + * @param {?proto.integration.JoinServerContext|undefined} value + * @return {!proto.integration.UplinkEvent} returns this +*/ +proto.integration.UplinkEvent.prototype.setJoinServerContext = function(value) { + return jspb.Message.setWrapperField(this, 15, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.integration.UplinkEvent} returns this + */ +proto.integration.UplinkEvent.prototype.clearJoinServerContext = function() { + return this.setJoinServerContext(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.integration.UplinkEvent.prototype.hasJoinServerContext = function() { + return jspb.Message.getField(this, 15) != null; +}; + + @@ -1662,7 +1916,8 @@ proto.integration.JoinEvent.toObject = function(includeInstance, msg) { time: (f = msg.getTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), deviceInfo: (f = msg.getDeviceInfo()) && proto.integration.DeviceInfo.toObject(includeInstance, f), devAddr: jspb.Message.getFieldWithDefault(msg, 4, ""), - relayRxInfo: (f = msg.getRelayRxInfo()) && proto.integration.UplinkRelayRxInfo.toObject(includeInstance, f) + relayRxInfo: (f = msg.getRelayRxInfo()) && proto.integration.UplinkRelayRxInfo.toObject(includeInstance, f), + joinServerContext: (f = msg.getJoinServerContext()) && proto.integration.JoinServerContext.toObject(includeInstance, f) }; if (includeInstance) { @@ -1722,6 +1977,11 @@ proto.integration.JoinEvent.deserializeBinaryFromReader = function(msg, reader) reader.readMessage(value,proto.integration.UplinkRelayRxInfo.deserializeBinaryFromReader); msg.setRelayRxInfo(value); break; + case 6: + var value = new proto.integration.JoinServerContext; + reader.readMessage(value,proto.integration.JoinServerContext.deserializeBinaryFromReader); + msg.setJoinServerContext(value); + break; default: reader.skipField(); break; @@ -1789,6 +2049,14 @@ proto.integration.JoinEvent.serializeBinaryToWriter = function(message, writer) proto.integration.UplinkRelayRxInfo.serializeBinaryToWriter ); } + f = message.getJoinServerContext(); + if (f != null) { + writer.writeMessage( + 6, + f, + proto.integration.JoinServerContext.serializeBinaryToWriter + ); + } }; @@ -1939,6 +2207,43 @@ proto.integration.JoinEvent.prototype.hasRelayRxInfo = function() { }; +/** + * optional JoinServerContext join_server_context = 6; + * @return {?proto.integration.JoinServerContext} + */ +proto.integration.JoinEvent.prototype.getJoinServerContext = function() { + return /** @type{?proto.integration.JoinServerContext} */ ( + jspb.Message.getWrapperField(this, proto.integration.JoinServerContext, 6)); +}; + + +/** + * @param {?proto.integration.JoinServerContext|undefined} value + * @return {!proto.integration.JoinEvent} returns this +*/ +proto.integration.JoinEvent.prototype.setJoinServerContext = function(value) { + return jspb.Message.setWrapperField(this, 6, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.integration.JoinEvent} returns this + */ +proto.integration.JoinEvent.prototype.clearJoinServerContext = function() { + return this.setJoinServerContext(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.integration.JoinEvent.prototype.hasJoinServerContext = function() { + return jspb.Message.getField(this, 6) != null; +}; + + @@ -4284,7 +4589,8 @@ proto.integration.LogCode = { UPLINK_MIC: 6, UPLINK_F_CNT_RETRANSMISSION: 7, DOWNLINK_GATEWAY: 8, - RELAY_NEW_END_DEVICE: 9 + RELAY_NEW_END_DEVICE: 9, + F_CNT_DOWN: 10 }; goog.object.extend(exports, proto.integration); diff --git a/api/md/api/api.md b/api/md/api/api.md index 30b91aa3..af7a0a04 100644 --- a/api/md/api/api.md +++ b/api/md/api/api.md @@ -119,6 +119,8 @@ - [GetDeviceMetricsResponse](#api-GetDeviceMetricsResponse) - [GetDeviceMetricsResponse.MetricsEntry](#api-GetDeviceMetricsResponse-MetricsEntry) - [GetDeviceMetricsResponse.StatesEntry](#api-GetDeviceMetricsResponse-StatesEntry) + - [GetDeviceNextFCntDownRequest](#api-GetDeviceNextFCntDownRequest) + - [GetDeviceNextFCntDownResponse](#api-GetDeviceNextFCntDownResponse) - [GetDeviceQueueItemsRequest](#api-GetDeviceQueueItemsRequest) - [GetDeviceQueueItemsResponse](#api-GetDeviceQueueItemsResponse) - [GetDeviceRequest](#api-GetDeviceRequest) @@ -1841,8 +1843,9 @@ applications. | f_port | [uint32](#uint32) | | FPort (must be > 0). | | data | [bytes](#bytes) | | Data. Or use the json_object field when a codec has been configured. | | object | [google.protobuf.Struct](#google-protobuf-Struct) | | Only use this when a codec has been configured that can encode this object to bytes. | -| is_pending | [bool](#bool) | | Is pending. This is set to true when the downlink is pending. | -| f_cnt_down | [uint32](#uint32) | | Downlink frame-counter. This is set when the payload has been sent as downlink. | +| is_pending | [bool](#bool) | | Is pending. This is set by ChirpStack to true when the downlink is pending (e.g. it has been sent, but a confirmation is still pending). | +| f_cnt_down | [uint32](#uint32) | | Downlink frame-counter. Do not set this for plain-text data payloads. It will be automatically set by ChirpStack when the payload has been sent as downlink. | +| is_encrypted | [bool](#bool) | | Is encrypted. This must be set to true if the end-application has already encrypted the data payload. In this case, the f_cnt_down field must be set to the corresponding frame-counter which has been used during the encryption. | @@ -2108,6 +2111,36 @@ applications. + + +### GetDeviceNextFCntDownRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| dev_eui | [string](#string) | | Device EUI (EUI64). | + + + + + + + + +### GetDeviceNextFCntDownResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| f_cnt_down | [uint32](#uint32) | | FCntDown. | + + + + + + ### GetDeviceQueueItemsRequest @@ -2302,6 +2335,7 @@ DeviceService is the service providing API methods for managing devices. | Enqueue | [EnqueueDeviceQueueItemRequest](#api-EnqueueDeviceQueueItemRequest) | [EnqueueDeviceQueueItemResponse](#api-EnqueueDeviceQueueItemResponse) | Enqueue adds the given item to the downlink queue. | | FlushQueue | [FlushDeviceQueueRequest](#api-FlushDeviceQueueRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | FlushQueue flushes the downlink device-queue. | | GetQueue | [GetDeviceQueueItemsRequest](#api-GetDeviceQueueItemsRequest) | [GetDeviceQueueItemsResponse](#api-GetDeviceQueueItemsResponse) | GetQueue returns the downlink device-queue. | +| GetNextFCntDown | [GetDeviceNextFCntDownRequest](#api-GetDeviceNextFCntDownRequest) | [GetDeviceNextFCntDownResponse](#api-GetDeviceNextFCntDownResponse) | GetNextFCntDown returns the next FCntDown to use for enqueing encrypted downlinks. The difference with the DeviceActivation f_cont_down is that this method takes potential existing queue-items into account. | diff --git a/api/proto/api/device.proto b/api/proto/api/device.proto index bdd8796f..0d739a0c 100644 --- a/api/proto/api/device.proto +++ b/api/proto/api/device.proto @@ -165,6 +165,17 @@ service DeviceService { get : "/api/devices/{dev_eui}/queue" }; } + + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + // downlinks. The difference with the DeviceActivation f_cont_down is that + // this method takes potential existing queue-items into account. + rpc GetNextFCntDown(GetDeviceNextFCntDownRequest) + returns (GetDeviceNextFCntDownResponse) { + option (google.api.http) = { + post : "/api/devices/{dev_eui}/get-next-f-cnt-down" + body : "*" + }; + } } message Device { @@ -504,12 +515,20 @@ message DeviceQueueItem { google.protobuf.Struct object = 6; // Is pending. - // This is set to true when the downlink is pending. + // This is set by ChirpStack to true when the downlink is pending (e.g. it + // has been sent, but a confirmation is still pending). bool is_pending = 7; // Downlink frame-counter. - // This is set when the payload has been sent as downlink. + // Do not set this for plain-text data payloads. It will be automatically set + // by ChirpStack when the payload has been sent as downlink. uint32 f_cnt_down = 8; + + // Is encrypted. + // This must be set to true if the end-application has already encrypted + // the data payload. In this case, the f_cnt_down field must be set to + // the corresponding frame-counter which has been used during the encryption. + bool is_encrypted = 9; } message EnqueueDeviceQueueItemRequest { DeviceQueueItem queue_item = 1; } @@ -544,3 +563,13 @@ message FlushDevNoncesRequest { // Device EUI (EUI64). string dev_eui = 1; } + +message GetDeviceNextFCntDownRequest { + // Device EUI (EUI64). + string dev_eui = 1; +} + +message GetDeviceNextFCntDownResponse { + // FCntDown. + uint32 f_cnt_down = 1; +} \ No newline at end of file diff --git a/api/proto/integration/integration.proto b/api/proto/integration/integration.proto index 93ddc020..552ddb2f 100644 --- a/api/proto/integration/integration.proto +++ b/api/proto/integration/integration.proto @@ -55,6 +55,9 @@ enum LogCode { // Relay new end-device. RELAY_NEW_END_DEVICE = 9; + + // Downlink frame-counter. + F_CNT_DOWN = 10; } // Device information. @@ -111,6 +114,15 @@ message UplinkRelayRxInfo { uint32 wor_channel = 6; } +// Join-Server context. +message JoinServerContext { + // Session-key ID. + string session_key_id = 1; + + // AppSKey envelope. + common.KeyEnvelope app_s_key = 2; +} + // UplinkEvent is the message sent when an uplink payload has been received. message UplinkEvent { // Deduplication ID (UUID). @@ -155,6 +167,12 @@ message UplinkEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 14; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 15; } // JoinEvent is the message sent when a device joined the network. @@ -174,6 +192,12 @@ message JoinEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 5; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 6; } // AckEvent is the message sent when a confirmation on a confirmed downlink diff --git a/api/proto/internal/internal.proto b/api/proto/internal/internal.proto index d35f000b..e3483e1b 100644 --- a/api/proto/internal/internal.proto +++ b/api/proto/internal/internal.proto @@ -31,6 +31,9 @@ message DeviceSession { // AppSKey envelope. common.KeyEnvelope app_s_key = 8; + // JS Session Key ID. + bytes js_session_key_id = 42; + // Uplink frame-counter. uint32 f_cnt_up = 9; diff --git a/api/python/proto/chirpstack-api/api/device.proto b/api/python/proto/chirpstack-api/api/device.proto index 3763d5e4..76cad82d 100644 --- a/api/python/proto/chirpstack-api/api/device.proto +++ b/api/python/proto/chirpstack-api/api/device.proto @@ -165,6 +165,17 @@ service DeviceService { get : "/api/devices/{dev_eui}/queue" }; } + + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + // downlinks. The difference with the DeviceActivation f_cont_down is that + // this method takes potential existing queue-items into account. + rpc GetNextFCntDown(GetDeviceNextFCntDownRequest) + returns (GetDeviceNextFCntDownResponse) { + option (google.api.http) = { + post : "/api/devices/{dev_eui}/get-next-f-cnt-down" + body : "*" + }; + } } message Device { @@ -504,12 +515,20 @@ message DeviceQueueItem { google.protobuf.Struct object = 6; // Is pending. - // This is set to true when the downlink is pending. + // This is set by ChirpStack to true when the downlink is pending (e.g. it + // has been sent, but a confirmation is still pending). bool is_pending = 7; // Downlink frame-counter. - // This is set when the payload has been sent as downlink. + // Do not set this for plain-text data payloads. It will be automatically set + // by ChirpStack when the payload has been sent as downlink. uint32 f_cnt_down = 8; + + // Is encrypted. + // This must be set to true if the end-application has already encrypted + // the data payload. In this case, the f_cnt_down field must be set to + // the corresponding frame-counter which has been used during the encryption. + bool is_encrypted = 9; } message EnqueueDeviceQueueItemRequest { DeviceQueueItem queue_item = 1; } @@ -544,3 +563,13 @@ message FlushDevNoncesRequest { // Device EUI (EUI64). string dev_eui = 1; } + +message GetDeviceNextFCntDownRequest { + // Device EUI (EUI64). + string dev_eui = 1; +} + +message GetDeviceNextFCntDownResponse { + // FCntDown. + uint32 f_cnt_down = 1; +} \ No newline at end of file diff --git a/api/python/proto/chirpstack-api/integration/integration.proto b/api/python/proto/chirpstack-api/integration/integration.proto index 906602b6..b302272a 100644 --- a/api/python/proto/chirpstack-api/integration/integration.proto +++ b/api/python/proto/chirpstack-api/integration/integration.proto @@ -55,6 +55,9 @@ enum LogCode { // Relay new end-device. RELAY_NEW_END_DEVICE = 9; + + // Downlink frame-counter. + F_CNT_DOWN = 10; } // Device information. @@ -111,6 +114,15 @@ message UplinkRelayRxInfo { uint32 wor_channel = 6; } +// Join-Server context. +message JoinServerContext { + // Session-key ID. + string session_key_id = 1; + + // AppSKey envelope. + common.KeyEnvelope app_s_key = 2; +} + // UplinkEvent is the message sent when an uplink payload has been received. message UplinkEvent { // Deduplication ID (UUID). @@ -155,6 +167,12 @@ message UplinkEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 14; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 15; } // JoinEvent is the message sent when a device joined the network. @@ -174,6 +192,12 @@ message JoinEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 5; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 6; } // AckEvent is the message sent when a confirmation on a confirmed downlink diff --git a/api/python/proto/chirpstack-api/internal/internal.proto b/api/python/proto/chirpstack-api/internal/internal.proto index 2403d2d9..6d2e2b1f 100644 --- a/api/python/proto/chirpstack-api/internal/internal.proto +++ b/api/python/proto/chirpstack-api/internal/internal.proto @@ -31,6 +31,9 @@ message DeviceSession { // AppSKey envelope. common.KeyEnvelope app_s_key = 8; + // JS Session Key ID. + bytes js_session_key_id = 42; + // Uplink frame-counter. uint32 f_cnt_up = 9; diff --git a/api/python/src/chirpstack_api/api/device_pb2.py b/api/python/src/chirpstack_api/api/device_pb2.py index f9e339e7..617b074d 100644 --- a/api/python/src/chirpstack_api/api/device_pb2.py +++ b/api/python/src/chirpstack_api/api/device_pb2.py @@ -18,7 +18,7 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63hirpstack-api/api/device.proto\x12\x03\x61pi\x1a\"chirpstack-api/common/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xe2\x02\n\x06\x44\x65vice\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x04 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x05 \x01(\t\x12\x17\n\x0fskip_fcnt_check\x18\x06 \x01(\x08\x12\x13\n\x0bis_disabled\x18\x07 \x01(\x08\x12-\n\tvariables\x18\x08 \x03(\x0b\x32\x1a.api.Device.VariablesEntry\x12#\n\x04tags\x18\t \x03(\x0b\x32\x15.api.Device.TagsEntry\x12\x10\n\x08join_eui\x18\n \x01(\t\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"T\n\x0c\x44\x65viceStatus\x12\x0e\n\x06margin\x18\x01 \x01(\x05\x12\x1d\n\x15\x65xternal_power_source\x18\x02 \x01(\x08\x12\x15\n\rbattery_level\x18\x03 \x01(\x02\"\xb8\x02\n\x0e\x44\x65viceListItem\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0clast_seen_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x07 \x01(\t\x12\x1b\n\x13\x64\x65vice_profile_name\x18\x08 \x01(\t\x12(\n\rdevice_status\x18\t \x01(\x0b\x32\x11.api.DeviceStatus\"?\n\nDeviceKeys\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x0f\n\x07nwk_key\x18\x02 \x01(\t\x12\x0f\n\x07\x61pp_key\x18\x03 \x01(\t\"2\n\x13\x43reateDeviceRequest\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\"#\n\x10GetDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\x98\x02\n\x11GetDeviceResponse\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0clast_seen_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\rdevice_status\x18\x05 \x01(\x0b\x32\x11.api.DeviceStatus\x12*\n\rclass_enabled\x18\x06 \x01(\x0e\x32\x13.common.DeviceClass\"2\n\x13UpdateDeviceRequest\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\"&\n\x13\x44\x65leteDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"w\n\x12ListDevicesRequest\x12\r\n\x05limit\x18\x01 \x01(\r\x12\x0e\n\x06offset\x18\x02 \x01(\r\x12\x0e\n\x06search\x18\x03 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x04 \x01(\t\x12\x1a\n\x12multicast_group_id\x18\x05 \x01(\t\"O\n\x13ListDevicesResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12#\n\x06result\x18\x02 \x03(\x0b\x32\x13.api.DeviceListItem\"?\n\x17\x43reateDeviceKeysRequest\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\"\'\n\x14GetDeviceKeysRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\x9d\x01\n\x15GetDeviceKeysResponse\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"?\n\x17UpdateDeviceKeysRequest\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\"*\n\x17\x44\x65leteDeviceKeysRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\xcf\x01\n\x10\x44\x65viceActivation\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x10\n\x08\x64\x65v_addr\x18\x02 \x01(\t\x12\x11\n\tapp_s_key\x18\x03 \x01(\t\x12\x15\n\rnwk_s_enc_key\x18\x04 \x01(\t\x12\x17\n\x0fs_nwk_s_int_key\x18\x08 \x01(\t\x12\x17\n\x0f\x66_nwk_s_int_key\x18\t \x01(\t\x12\x10\n\x08\x66_cnt_up\x18\x05 \x01(\r\x12\x14\n\x0cn_f_cnt_down\x18\x06 \x01(\r\x12\x14\n\x0c\x61_f_cnt_down\x18\n \x01(\r\"I\n\x15\x41\x63tivateDeviceRequest\x12\x30\n\x11\x64\x65vice_activation\x18\x01 \x01(\x0b\x32\x15.api.DeviceActivation\"*\n\x17\x44\x65\x61\x63tivateDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"-\n\x1aGetDeviceActivationRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"O\n\x1bGetDeviceActivationResponse\x12\x30\n\x11\x64\x65vice_activation\x18\x01 \x01(\x0b\x32\x15.api.DeviceActivation\"*\n\x17GetRandomDevAddrRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\",\n\x18GetRandomDevAddrResponse\x12\x10\n\x08\x64\x65v_addr\x18\x01 \x01(\t\"\xa8\x01\n\x17GetDeviceMetricsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12)\n\x05start\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\x0b\x61ggregation\x18\x04 \x01(\x0e\x32\x13.common.Aggregation\"\x93\x02\n\x18GetDeviceMetricsResponse\x12;\n\x07metrics\x18\x01 \x03(\x0b\x32*.api.GetDeviceMetricsResponse.MetricsEntry\x12\x39\n\x06states\x18\x02 \x03(\x0b\x32).api.GetDeviceMetricsResponse.StatesEntry\x1a>\n\x0cMetricsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.common.Metric:\x02\x38\x01\x1a?\n\x0bStatesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1f\n\x05value\x18\x02 \x01(\x0b\x32\x10.api.DeviceState:\x02\x38\x01\"*\n\x0b\x44\x65viceState\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"\xac\x01\n\x1bGetDeviceLinkMetricsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12)\n\x05start\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\x0b\x61ggregation\x18\x04 \x01(\x0e\x32\x13.common.Aggregation\"\xfb\x01\n\x1cGetDeviceLinkMetricsResponse\x12\"\n\nrx_packets\x18\x01 \x01(\x0b\x32\x0e.common.Metric\x12\x1f\n\x07gw_rssi\x18\x02 \x01(\x0b\x32\x0e.common.Metric\x12\x1e\n\x06gw_snr\x18\x03 \x01(\x0b\x32\x0e.common.Metric\x12+\n\x13rx_packets_per_freq\x18\x04 \x01(\x0b\x32\x0e.common.Metric\x12)\n\x11rx_packets_per_dr\x18\x05 \x01(\x0b\x32\x0e.common.Metric\x12\x1e\n\x06\x65rrors\x18\x06 \x01(\x0b\x32\x0e.common.Metric\"\xb0\x01\n\x0f\x44\x65viceQueueItem\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x02 \x01(\t\x12\x11\n\tconfirmed\x18\x03 \x01(\x08\x12\x0e\n\x06\x66_port\x18\x04 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x12\n\nis_pending\x18\x07 \x01(\x08\x12\x12\n\nf_cnt_down\x18\x08 \x01(\r\"I\n\x1d\x45nqueueDeviceQueueItemRequest\x12(\n\nqueue_item\x18\x01 \x01(\x0b\x32\x14.api.DeviceQueueItem\",\n\x1e\x45nqueueDeviceQueueItemResponse\x12\n\n\x02id\x18\x01 \x01(\t\"*\n\x17\x46lushDeviceQueueRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"A\n\x1aGetDeviceQueueItemsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x12\n\ncount_only\x18\x02 \x01(\x08\"X\n\x1bGetDeviceQueueItemsResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12$\n\x06result\x18\x02 \x03(\x0b\x32\x14.api.DeviceQueueItem\"(\n\x15\x46lushDevNoncesRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t2\xd0\x10\n\rDeviceService\x12S\n\x06\x43reate\x12\x18.api.CreateDeviceRequest\x1a\x16.google.protobuf.Empty\"\x17\x82\xd3\xe4\x93\x02\x11\"\x0c/api/devices:\x01*\x12T\n\x03Get\x12\x15.api.GetDeviceRequest\x1a\x16.api.GetDeviceResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\x12\x16/api/devices/{dev_eui}\x12\x64\n\x06Update\x12\x18.api.UpdateDeviceRequest\x1a\x16.google.protobuf.Empty\"(\x82\xd3\xe4\x93\x02\"\x1a\x1d/api/devices/{device.dev_eui}:\x01*\x12Z\n\x06\x44\x65lete\x12\x18.api.DeleteDeviceRequest\x1a\x16.google.protobuf.Empty\"\x1e\x82\xd3\xe4\x93\x02\x18*\x16/api/devices/{dev_eui}\x12O\n\x04List\x12\x17.api.ListDevicesRequest\x1a\x18.api.ListDevicesResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\x0c/api/devices\x12v\n\nCreateKeys\x12\x1c.api.CreateDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"2\x82\xd3\xe4\x93\x02,\"\'/api/devices/{device_keys.dev_eui}/keys:\x01*\x12\x65\n\x07GetKeys\x12\x19.api.GetDeviceKeysRequest\x1a\x1a.api.GetDeviceKeysResponse\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/api/devices/{dev_eui}/keys\x12v\n\nUpdateKeys\x12\x1c.api.UpdateDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"2\x82\xd3\xe4\x93\x02,\x1a\'/api/devices/{device_keys.dev_eui}/keys:\x01*\x12g\n\nDeleteKeys\x12\x1c.api.DeleteDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"#\x82\xd3\xe4\x93\x02\x1d*\x1b/api/devices/{dev_eui}/keys\x12o\n\x0e\x46lushDevNonces\x12\x1a.api.FlushDevNoncesRequest\x1a\x16.google.protobuf.Empty\")\x82\xd3\xe4\x93\x02#*!/api/devices/{dev_eui}/dev-nonces\x12|\n\x08\x41\x63tivate\x12\x1a.api.ActivateDeviceRequest\x1a\x16.google.protobuf.Empty\"<\x82\xd3\xe4\x93\x02\x36\"1/api/devices/{device_activation.dev_eui}/activate:\x01*\x12m\n\nDeactivate\x12\x1c.api.DeactivateDeviceRequest\x1a\x16.google.protobuf.Empty\")\x82\xd3\xe4\x93\x02#*!/api/devices/{dev_eui}/activation\x12}\n\rGetActivation\x12\x1f.api.GetDeviceActivationRequest\x1a .api.GetDeviceActivationResponse\")\x82\xd3\xe4\x93\x02#\x12!/api/devices/{dev_eui}/activation\x12\x83\x01\n\x10GetRandomDevAddr\x12\x1c.api.GetRandomDevAddrRequest\x1a\x1d.api.GetRandomDevAddrResponse\"2\x82\xd3\xe4\x93\x02,\"*/api/devices/{dev_eui}/get-random-dev-addr\x12q\n\nGetMetrics\x12\x1c.api.GetDeviceMetricsRequest\x1a\x1d.api.GetDeviceMetricsResponse\"&\x82\xd3\xe4\x93\x02 \x12\x1e/api/devices/{dev_eui}/metrics\x12\x82\x01\n\x0eGetLinkMetrics\x12 .api.GetDeviceLinkMetricsRequest\x1a!.api.GetDeviceLinkMetricsResponse\"+\x82\xd3\xe4\x93\x02%\x12#/api/devices/{dev_eui}/link-metrics\x12\x86\x01\n\x07\x45nqueue\x12\".api.EnqueueDeviceQueueItemRequest\x1a#.api.EnqueueDeviceQueueItemResponse\"2\x82\xd3\xe4\x93\x02,\"\'/api/devices/{queue_item.dev_eui}/queue:\x01*\x12h\n\nFlushQueue\x12\x1c.api.FlushDeviceQueueRequest\x1a\x16.google.protobuf.Empty\"$\x82\xd3\xe4\x93\x02\x1e*\x1c/api/devices/{dev_eui}/queue\x12s\n\x08GetQueue\x12\x1f.api.GetDeviceQueueItemsRequest\x1a .api.GetDeviceQueueItemsResponse\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/devices/{dev_eui}/queueBc\n\x11io.chirpstack.apiB\x0b\x44\x65viceProtoP\x01Z.github.com/chirpstack/chirpstack/api/go/v4/api\xaa\x02\x0e\x43hirpstack.Apib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63hirpstack-api/api/device.proto\x12\x03\x61pi\x1a\"chirpstack-api/common/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xe2\x02\n\x06\x44\x65vice\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x04 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x05 \x01(\t\x12\x17\n\x0fskip_fcnt_check\x18\x06 \x01(\x08\x12\x13\n\x0bis_disabled\x18\x07 \x01(\x08\x12-\n\tvariables\x18\x08 \x03(\x0b\x32\x1a.api.Device.VariablesEntry\x12#\n\x04tags\x18\t \x03(\x0b\x32\x15.api.Device.TagsEntry\x12\x10\n\x08join_eui\x18\n \x01(\t\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"T\n\x0c\x44\x65viceStatus\x12\x0e\n\x06margin\x18\x01 \x01(\x05\x12\x1d\n\x15\x65xternal_power_source\x18\x02 \x01(\x08\x12\x15\n\rbattery_level\x18\x03 \x01(\x02\"\xb8\x02\n\x0e\x44\x65viceListItem\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0clast_seen_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x07 \x01(\t\x12\x1b\n\x13\x64\x65vice_profile_name\x18\x08 \x01(\t\x12(\n\rdevice_status\x18\t \x01(\x0b\x32\x11.api.DeviceStatus\"?\n\nDeviceKeys\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x0f\n\x07nwk_key\x18\x02 \x01(\t\x12\x0f\n\x07\x61pp_key\x18\x03 \x01(\t\"2\n\x13\x43reateDeviceRequest\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\"#\n\x10GetDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\x98\x02\n\x11GetDeviceResponse\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0clast_seen_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\rdevice_status\x18\x05 \x01(\x0b\x32\x11.api.DeviceStatus\x12*\n\rclass_enabled\x18\x06 \x01(\x0e\x32\x13.common.DeviceClass\"2\n\x13UpdateDeviceRequest\x12\x1b\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x0b.api.Device\"&\n\x13\x44\x65leteDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"w\n\x12ListDevicesRequest\x12\r\n\x05limit\x18\x01 \x01(\r\x12\x0e\n\x06offset\x18\x02 \x01(\r\x12\x0e\n\x06search\x18\x03 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x04 \x01(\t\x12\x1a\n\x12multicast_group_id\x18\x05 \x01(\t\"O\n\x13ListDevicesResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12#\n\x06result\x18\x02 \x03(\x0b\x32\x13.api.DeviceListItem\"?\n\x17\x43reateDeviceKeysRequest\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\"\'\n\x14GetDeviceKeysRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\x9d\x01\n\x15GetDeviceKeysResponse\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"?\n\x17UpdateDeviceKeysRequest\x12$\n\x0b\x64\x65vice_keys\x18\x01 \x01(\x0b\x32\x0f.api.DeviceKeys\"*\n\x17\x44\x65leteDeviceKeysRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"\xcf\x01\n\x10\x44\x65viceActivation\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x10\n\x08\x64\x65v_addr\x18\x02 \x01(\t\x12\x11\n\tapp_s_key\x18\x03 \x01(\t\x12\x15\n\rnwk_s_enc_key\x18\x04 \x01(\t\x12\x17\n\x0fs_nwk_s_int_key\x18\x08 \x01(\t\x12\x17\n\x0f\x66_nwk_s_int_key\x18\t \x01(\t\x12\x10\n\x08\x66_cnt_up\x18\x05 \x01(\r\x12\x14\n\x0cn_f_cnt_down\x18\x06 \x01(\r\x12\x14\n\x0c\x61_f_cnt_down\x18\n \x01(\r\"I\n\x15\x41\x63tivateDeviceRequest\x12\x30\n\x11\x64\x65vice_activation\x18\x01 \x01(\x0b\x32\x15.api.DeviceActivation\"*\n\x17\x44\x65\x61\x63tivateDeviceRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"-\n\x1aGetDeviceActivationRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"O\n\x1bGetDeviceActivationResponse\x12\x30\n\x11\x64\x65vice_activation\x18\x01 \x01(\x0b\x32\x15.api.DeviceActivation\"*\n\x17GetRandomDevAddrRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\",\n\x18GetRandomDevAddrResponse\x12\x10\n\x08\x64\x65v_addr\x18\x01 \x01(\t\"\xa8\x01\n\x17GetDeviceMetricsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12)\n\x05start\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\x0b\x61ggregation\x18\x04 \x01(\x0e\x32\x13.common.Aggregation\"\x93\x02\n\x18GetDeviceMetricsResponse\x12;\n\x07metrics\x18\x01 \x03(\x0b\x32*.api.GetDeviceMetricsResponse.MetricsEntry\x12\x39\n\x06states\x18\x02 \x03(\x0b\x32).api.GetDeviceMetricsResponse.StatesEntry\x1a>\n\x0cMetricsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.common.Metric:\x02\x38\x01\x1a?\n\x0bStatesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1f\n\x05value\x18\x02 \x01(\x0b\x32\x10.api.DeviceState:\x02\x38\x01\"*\n\x0b\x44\x65viceState\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"\xac\x01\n\x1bGetDeviceLinkMetricsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12)\n\x05start\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\x0b\x61ggregation\x18\x04 \x01(\x0e\x32\x13.common.Aggregation\"\xfb\x01\n\x1cGetDeviceLinkMetricsResponse\x12\"\n\nrx_packets\x18\x01 \x01(\x0b\x32\x0e.common.Metric\x12\x1f\n\x07gw_rssi\x18\x02 \x01(\x0b\x32\x0e.common.Metric\x12\x1e\n\x06gw_snr\x18\x03 \x01(\x0b\x32\x0e.common.Metric\x12+\n\x13rx_packets_per_freq\x18\x04 \x01(\x0b\x32\x0e.common.Metric\x12)\n\x11rx_packets_per_dr\x18\x05 \x01(\x0b\x32\x0e.common.Metric\x12\x1e\n\x06\x65rrors\x18\x06 \x01(\x0b\x32\x0e.common.Metric\"\xc6\x01\n\x0f\x44\x65viceQueueItem\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x02 \x01(\t\x12\x11\n\tconfirmed\x18\x03 \x01(\x08\x12\x0e\n\x06\x66_port\x18\x04 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x12\n\nis_pending\x18\x07 \x01(\x08\x12\x12\n\nf_cnt_down\x18\x08 \x01(\r\x12\x14\n\x0cis_encrypted\x18\t \x01(\x08\"I\n\x1d\x45nqueueDeviceQueueItemRequest\x12(\n\nqueue_item\x18\x01 \x01(\x0b\x32\x14.api.DeviceQueueItem\",\n\x1e\x45nqueueDeviceQueueItemResponse\x12\n\n\x02id\x18\x01 \x01(\t\"*\n\x17\x46lushDeviceQueueRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"A\n\x1aGetDeviceQueueItemsRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x12\n\ncount_only\x18\x02 \x01(\x08\"X\n\x1bGetDeviceQueueItemsResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12$\n\x06result\x18\x02 \x03(\x0b\x32\x14.api.DeviceQueueItem\"(\n\x15\x46lushDevNoncesRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"/\n\x1cGetDeviceNextFCntDownRequest\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\"3\n\x1dGetDeviceNextFCntDownResponse\x12\x12\n\nf_cnt_down\x18\x01 \x01(\r2\xe2\x11\n\rDeviceService\x12S\n\x06\x43reate\x12\x18.api.CreateDeviceRequest\x1a\x16.google.protobuf.Empty\"\x17\x82\xd3\xe4\x93\x02\x11\"\x0c/api/devices:\x01*\x12T\n\x03Get\x12\x15.api.GetDeviceRequest\x1a\x16.api.GetDeviceResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\x12\x16/api/devices/{dev_eui}\x12\x64\n\x06Update\x12\x18.api.UpdateDeviceRequest\x1a\x16.google.protobuf.Empty\"(\x82\xd3\xe4\x93\x02\"\x1a\x1d/api/devices/{device.dev_eui}:\x01*\x12Z\n\x06\x44\x65lete\x12\x18.api.DeleteDeviceRequest\x1a\x16.google.protobuf.Empty\"\x1e\x82\xd3\xe4\x93\x02\x18*\x16/api/devices/{dev_eui}\x12O\n\x04List\x12\x17.api.ListDevicesRequest\x1a\x18.api.ListDevicesResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\x0c/api/devices\x12v\n\nCreateKeys\x12\x1c.api.CreateDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"2\x82\xd3\xe4\x93\x02,\"\'/api/devices/{device_keys.dev_eui}/keys:\x01*\x12\x65\n\x07GetKeys\x12\x19.api.GetDeviceKeysRequest\x1a\x1a.api.GetDeviceKeysResponse\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/api/devices/{dev_eui}/keys\x12v\n\nUpdateKeys\x12\x1c.api.UpdateDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"2\x82\xd3\xe4\x93\x02,\x1a\'/api/devices/{device_keys.dev_eui}/keys:\x01*\x12g\n\nDeleteKeys\x12\x1c.api.DeleteDeviceKeysRequest\x1a\x16.google.protobuf.Empty\"#\x82\xd3\xe4\x93\x02\x1d*\x1b/api/devices/{dev_eui}/keys\x12o\n\x0e\x46lushDevNonces\x12\x1a.api.FlushDevNoncesRequest\x1a\x16.google.protobuf.Empty\")\x82\xd3\xe4\x93\x02#*!/api/devices/{dev_eui}/dev-nonces\x12|\n\x08\x41\x63tivate\x12\x1a.api.ActivateDeviceRequest\x1a\x16.google.protobuf.Empty\"<\x82\xd3\xe4\x93\x02\x36\"1/api/devices/{device_activation.dev_eui}/activate:\x01*\x12m\n\nDeactivate\x12\x1c.api.DeactivateDeviceRequest\x1a\x16.google.protobuf.Empty\")\x82\xd3\xe4\x93\x02#*!/api/devices/{dev_eui}/activation\x12}\n\rGetActivation\x12\x1f.api.GetDeviceActivationRequest\x1a .api.GetDeviceActivationResponse\")\x82\xd3\xe4\x93\x02#\x12!/api/devices/{dev_eui}/activation\x12\x83\x01\n\x10GetRandomDevAddr\x12\x1c.api.GetRandomDevAddrRequest\x1a\x1d.api.GetRandomDevAddrResponse\"2\x82\xd3\xe4\x93\x02,\"*/api/devices/{dev_eui}/get-random-dev-addr\x12q\n\nGetMetrics\x12\x1c.api.GetDeviceMetricsRequest\x1a\x1d.api.GetDeviceMetricsResponse\"&\x82\xd3\xe4\x93\x02 \x12\x1e/api/devices/{dev_eui}/metrics\x12\x82\x01\n\x0eGetLinkMetrics\x12 .api.GetDeviceLinkMetricsRequest\x1a!.api.GetDeviceLinkMetricsResponse\"+\x82\xd3\xe4\x93\x02%\x12#/api/devices/{dev_eui}/link-metrics\x12\x86\x01\n\x07\x45nqueue\x12\".api.EnqueueDeviceQueueItemRequest\x1a#.api.EnqueueDeviceQueueItemResponse\"2\x82\xd3\xe4\x93\x02,\"\'/api/devices/{queue_item.dev_eui}/queue:\x01*\x12h\n\nFlushQueue\x12\x1c.api.FlushDeviceQueueRequest\x1a\x16.google.protobuf.Empty\"$\x82\xd3\xe4\x93\x02\x1e*\x1c/api/devices/{dev_eui}/queue\x12s\n\x08GetQueue\x12\x1f.api.GetDeviceQueueItemsRequest\x1a .api.GetDeviceQueueItemsResponse\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/devices/{dev_eui}/queue\x12\x8f\x01\n\x0fGetNextFCntDown\x12!.api.GetDeviceNextFCntDownRequest\x1a\".api.GetDeviceNextFCntDownResponse\"5\x82\xd3\xe4\x93\x02/\"*/api/devices/{dev_eui}/get-next-f-cnt-down:\x01*Bc\n\x11io.chirpstack.apiB\x0b\x44\x65viceProtoP\x01Z.github.com/chirpstack/chirpstack/api/go/v4/api\xaa\x02\x0e\x43hirpstack.Apib\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -73,6 +73,8 @@ if _descriptor._USE_C_DESCRIPTORS == False: _DEVICESERVICE.methods_by_name['FlushQueue']._serialized_options = b'\202\323\344\223\002\036*\034/api/devices/{dev_eui}/queue' _DEVICESERVICE.methods_by_name['GetQueue']._options = None _DEVICESERVICE.methods_by_name['GetQueue']._serialized_options = b'\202\323\344\223\002\036\022\034/api/devices/{dev_eui}/queue' + _DEVICESERVICE.methods_by_name['GetNextFCntDown']._options = None + _DEVICESERVICE.methods_by_name['GetNextFCntDown']._serialized_options = b'\202\323\344\223\002/\"*/api/devices/{dev_eui}/get-next-f-cnt-down:\001*' _globals['_DEVICE']._serialized_start=199 _globals['_DEVICE']._serialized_end=553 _globals['_DEVICE_VARIABLESENTRY']._serialized_start=460 @@ -138,19 +140,23 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['_GETDEVICELINKMETRICSRESPONSE']._serialized_start=3278 _globals['_GETDEVICELINKMETRICSRESPONSE']._serialized_end=3529 _globals['_DEVICEQUEUEITEM']._serialized_start=3532 - _globals['_DEVICEQUEUEITEM']._serialized_end=3708 - _globals['_ENQUEUEDEVICEQUEUEITEMREQUEST']._serialized_start=3710 - _globals['_ENQUEUEDEVICEQUEUEITEMREQUEST']._serialized_end=3783 - _globals['_ENQUEUEDEVICEQUEUEITEMRESPONSE']._serialized_start=3785 - _globals['_ENQUEUEDEVICEQUEUEITEMRESPONSE']._serialized_end=3829 - _globals['_FLUSHDEVICEQUEUEREQUEST']._serialized_start=3831 - _globals['_FLUSHDEVICEQUEUEREQUEST']._serialized_end=3873 - _globals['_GETDEVICEQUEUEITEMSREQUEST']._serialized_start=3875 - _globals['_GETDEVICEQUEUEITEMSREQUEST']._serialized_end=3940 - _globals['_GETDEVICEQUEUEITEMSRESPONSE']._serialized_start=3942 - _globals['_GETDEVICEQUEUEITEMSRESPONSE']._serialized_end=4030 - _globals['_FLUSHDEVNONCESREQUEST']._serialized_start=4032 - _globals['_FLUSHDEVNONCESREQUEST']._serialized_end=4072 - _globals['_DEVICESERVICE']._serialized_start=4075 - _globals['_DEVICESERVICE']._serialized_end=6203 + _globals['_DEVICEQUEUEITEM']._serialized_end=3730 + _globals['_ENQUEUEDEVICEQUEUEITEMREQUEST']._serialized_start=3732 + _globals['_ENQUEUEDEVICEQUEUEITEMREQUEST']._serialized_end=3805 + _globals['_ENQUEUEDEVICEQUEUEITEMRESPONSE']._serialized_start=3807 + _globals['_ENQUEUEDEVICEQUEUEITEMRESPONSE']._serialized_end=3851 + _globals['_FLUSHDEVICEQUEUEREQUEST']._serialized_start=3853 + _globals['_FLUSHDEVICEQUEUEREQUEST']._serialized_end=3895 + _globals['_GETDEVICEQUEUEITEMSREQUEST']._serialized_start=3897 + _globals['_GETDEVICEQUEUEITEMSREQUEST']._serialized_end=3962 + _globals['_GETDEVICEQUEUEITEMSRESPONSE']._serialized_start=3964 + _globals['_GETDEVICEQUEUEITEMSRESPONSE']._serialized_end=4052 + _globals['_FLUSHDEVNONCESREQUEST']._serialized_start=4054 + _globals['_FLUSHDEVNONCESREQUEST']._serialized_end=4094 + _globals['_GETDEVICENEXTFCNTDOWNREQUEST']._serialized_start=4096 + _globals['_GETDEVICENEXTFCNTDOWNREQUEST']._serialized_end=4143 + _globals['_GETDEVICENEXTFCNTDOWNRESPONSE']._serialized_start=4145 + _globals['_GETDEVICENEXTFCNTDOWNRESPONSE']._serialized_end=4196 + _globals['_DEVICESERVICE']._serialized_start=4199 + _globals['_DEVICESERVICE']._serialized_end=6473 # @@protoc_insertion_point(module_scope) diff --git a/api/python/src/chirpstack_api/api/device_pb2.pyi b/api/python/src/chirpstack_api/api/device_pb2.pyi index fa8283b7..181ba02c 100644 --- a/api/python/src/chirpstack_api/api/device_pb2.pyi +++ b/api/python/src/chirpstack_api/api/device_pb2.pyi @@ -315,7 +315,7 @@ class GetDeviceLinkMetricsResponse(_message.Message): def __init__(self, rx_packets: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ..., gw_rssi: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ..., gw_snr: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ..., rx_packets_per_freq: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ..., rx_packets_per_dr: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ..., errors: _Optional[_Union[_common_pb2.Metric, _Mapping]] = ...) -> None: ... class DeviceQueueItem(_message.Message): - __slots__ = ["id", "dev_eui", "confirmed", "f_port", "data", "object", "is_pending", "f_cnt_down"] + __slots__ = ["id", "dev_eui", "confirmed", "f_port", "data", "object", "is_pending", "f_cnt_down", "is_encrypted"] ID_FIELD_NUMBER: _ClassVar[int] DEV_EUI_FIELD_NUMBER: _ClassVar[int] CONFIRMED_FIELD_NUMBER: _ClassVar[int] @@ -324,6 +324,7 @@ class DeviceQueueItem(_message.Message): OBJECT_FIELD_NUMBER: _ClassVar[int] IS_PENDING_FIELD_NUMBER: _ClassVar[int] F_CNT_DOWN_FIELD_NUMBER: _ClassVar[int] + IS_ENCRYPTED_FIELD_NUMBER: _ClassVar[int] id: str dev_eui: str confirmed: bool @@ -332,7 +333,8 @@ class DeviceQueueItem(_message.Message): object: _struct_pb2.Struct is_pending: bool f_cnt_down: int - def __init__(self, id: _Optional[str] = ..., dev_eui: _Optional[str] = ..., confirmed: bool = ..., f_port: _Optional[int] = ..., data: _Optional[bytes] = ..., object: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., is_pending: bool = ..., f_cnt_down: _Optional[int] = ...) -> None: ... + is_encrypted: bool + def __init__(self, id: _Optional[str] = ..., dev_eui: _Optional[str] = ..., confirmed: bool = ..., f_port: _Optional[int] = ..., data: _Optional[bytes] = ..., object: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., is_pending: bool = ..., f_cnt_down: _Optional[int] = ..., is_encrypted: bool = ...) -> None: ... class EnqueueDeviceQueueItemRequest(_message.Message): __slots__ = ["queue_item"] @@ -373,3 +375,15 @@ class FlushDevNoncesRequest(_message.Message): DEV_EUI_FIELD_NUMBER: _ClassVar[int] dev_eui: str def __init__(self, dev_eui: _Optional[str] = ...) -> None: ... + +class GetDeviceNextFCntDownRequest(_message.Message): + __slots__ = ["dev_eui"] + DEV_EUI_FIELD_NUMBER: _ClassVar[int] + dev_eui: str + def __init__(self, dev_eui: _Optional[str] = ...) -> None: ... + +class GetDeviceNextFCntDownResponse(_message.Message): + __slots__ = ["f_cnt_down"] + F_CNT_DOWN_FIELD_NUMBER: _ClassVar[int] + f_cnt_down: int + def __init__(self, f_cnt_down: _Optional[int] = ...) -> None: ... diff --git a/api/python/src/chirpstack_api/api/device_pb2_grpc.py b/api/python/src/chirpstack_api/api/device_pb2_grpc.py index f401ff6f..c2c04ae4 100644 --- a/api/python/src/chirpstack_api/api/device_pb2_grpc.py +++ b/api/python/src/chirpstack_api/api/device_pb2_grpc.py @@ -111,6 +111,11 @@ class DeviceServiceStub(object): request_serializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceQueueItemsRequest.SerializeToString, response_deserializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceQueueItemsResponse.FromString, ) + self.GetNextFCntDown = channel.unary_unary( + '/api.DeviceService/GetNextFCntDown', + request_serializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownRequest.SerializeToString, + response_deserializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownResponse.FromString, + ) class DeviceServiceServicer(object): @@ -256,6 +261,15 @@ class DeviceServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetNextFCntDown(self, request, context): + """GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + downlinks. The difference with the DeviceActivation f_cont_down is that + this method takes potential existing queue-items into account. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_DeviceServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -354,6 +368,11 @@ def add_DeviceServiceServicer_to_server(servicer, server): request_deserializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceQueueItemsRequest.FromString, response_serializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceQueueItemsResponse.SerializeToString, ), + 'GetNextFCntDown': grpc.unary_unary_rpc_method_handler( + servicer.GetNextFCntDown, + request_deserializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownRequest.FromString, + response_serializer=chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'api.DeviceService', rpc_method_handlers) @@ -687,3 +706,20 @@ class DeviceService(object): chirpstack__api_dot_api_dot_device__pb2.GetDeviceQueueItemsResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetNextFCntDown(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/api.DeviceService/GetNextFCntDown', + chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownRequest.SerializeToString, + chirpstack__api_dot_api_dot_device__pb2.GetDeviceNextFCntDownResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/api/python/src/chirpstack_api/integration/integration_pb2.py b/api/python/src/chirpstack_api/integration/integration_pb2.py index 0ce0b58d..95d11014 100644 --- a/api/python/src/chirpstack_api/integration/integration_pb2.py +++ b/api/python/src/chirpstack_api/integration/integration_pb2.py @@ -17,7 +17,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,chirpstack-api/integration/integration.proto\x12\x0bintegration\x1a\"chirpstack-api/common/common.proto\x1a\x1a\x63hirpstack-api/gw/gw.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xd5\x02\n\nDeviceInfo\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x13\n\x0btenant_name\x18\x02 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x03 \x01(\t\x12\x18\n\x10\x61pplication_name\x18\x04 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x05 \x01(\t\x12\x1b\n\x13\x64\x65vice_profile_name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65vice_name\x18\x07 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x08 \x01(\t\x12\x31\n\x14\x64\x65vice_class_enabled\x18\n \x01(\x0e\x32\x13.common.DeviceClass\x12/\n\x04tags\x18\t \x03(\x0b\x32!.integration.DeviceInfo.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"s\n\x11UplinkRelayRxInfo\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x11\n\tfrequency\x18\x02 \x01(\r\x12\n\n\x02\x64r\x18\x03 \x01(\r\x12\x0b\n\x03snr\x18\x04 \x01(\x05\x12\x0c\n\x04rssi\x18\x05 \x01(\x05\x12\x13\n\x0bwor_channel\x18\x06 \x01(\r\"\x90\x03\n\x0bUplinkEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x10\n\x08\x64\x65v_addr\x18\x04 \x01(\t\x12\x0b\n\x03\x61\x64r\x18\x05 \x01(\x08\x12\n\n\x02\x64r\x18\x06 \x01(\r\x12\r\n\x05\x66_cnt\x18\x07 \x01(\r\x12\x0e\n\x06\x66_port\x18\x08 \x01(\r\x12\x11\n\tconfirmed\x18\t \x01(\x08\x12\x0c\n\x04\x64\x61ta\x18\n \x01(\x0c\x12\'\n\x06object\x18\x0b \x01(\x0b\x32\x17.google.protobuf.Struct\x12!\n\x07rx_info\x18\x0c \x03(\x0b\x32\x10.gw.UplinkRxInfo\x12!\n\x07tx_info\x18\r \x01(\x0b\x32\x10.gw.UplinkTxInfo\x12\x35\n\rrelay_rx_info\x18\x0e \x01(\x0b\x32\x1e.integration.UplinkRelayRxInfo\"\xc6\x01\n\tJoinEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x10\n\x08\x64\x65v_addr\x18\x04 \x01(\t\x12\x35\n\rrelay_rx_info\x18\x05 \x01(\x0b\x32\x1e.integration.UplinkRelayRxInfo\"\xbd\x01\n\x08\x41\x63kEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x15\n\rqueue_item_id\x18\x04 \x01(\t\x12\x14\n\x0c\x61\x63knowledged\x18\x05 \x01(\x08\x12\x12\n\nf_cnt_down\x18\x06 \x01(\r\"\xdd\x01\n\nTxAckEvent\x12\x13\n\x0b\x64ownlink_id\x18\x01 \x01(\r\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x15\n\rqueue_item_id\x18\x04 \x01(\t\x12\x12\n\nf_cnt_down\x18\x05 \x01(\r\x12\x12\n\ngateway_id\x18\x06 \x01(\t\x12#\n\x07tx_info\x18\x07 \x01(\x0b\x32\x12.gw.DownlinkTxInfo\"\xa6\x02\n\x08LogEvent\x12(\n\x04time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x17.integration.DeviceInfo\x12$\n\x05level\x18\x03 \x01(\x0e\x32\x15.integration.LogLevel\x12\"\n\x04\x63ode\x18\x04 \x01(\x0e\x32\x14.integration.LogCode\x12\x13\n\x0b\x64\x65scription\x18\x05 \x01(\t\x12\x33\n\x07\x63ontext\x18\x06 \x03(\x0b\x32\".integration.LogEvent.ContextEntry\x1a.\n\x0c\x43ontextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe8\x01\n\x0bStatusEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x0e\n\x06margin\x18\x05 \x01(\x05\x12\x1d\n\x15\x65xternal_power_source\x18\x06 \x01(\x08\x12!\n\x19\x62\x61ttery_level_unavailable\x18\x07 \x01(\x08\x12\x15\n\rbattery_level\x18\x08 \x01(\x02\"\xa5\x01\n\rLocationEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\"\n\x08location\x18\x04 \x01(\x0b\x32\x10.common.Location\"\xdb\x01\n\x10IntegrationEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x18\n\x10integration_name\x18\x04 \x01(\t\x12\x12\n\nevent_type\x18\x05 \x01(\t\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x88\x01\n\x0f\x44ownlinkCommand\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x02 \x01(\t\x12\x11\n\tconfirmed\x18\x03 \x01(\x08\x12\x0e\n\x06\x66_port\x18\x04 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct*,\n\x08LogLevel\x12\x08\n\x04INFO\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02*\xda\x01\n\x07LogCode\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x19\n\x15\x44OWNLINK_PAYLOAD_SIZE\x10\x01\x12\x10\n\x0cUPLINK_CODEC\x10\x02\x12\x12\n\x0e\x44OWNLINK_CODEC\x10\x03\x12\x08\n\x04OTAA\x10\x04\x12\x16\n\x12UPLINK_F_CNT_RESET\x10\x05\x12\x0e\n\nUPLINK_MIC\x10\x06\x12\x1f\n\x1bUPLINK_F_CNT_RETRANSMISSION\x10\x07\x12\x14\n\x10\x44OWNLINK_GATEWAY\x10\x08\x12\x18\n\x14RELAY_NEW_END_DEVICE\x10\tB\x81\x01\n\x1dio.chirpstack.api.integrationB\x10IntegrationProtoP\x01Z3github.com/brocaar/chirpstack/api/go/v4/integration\xaa\x02\x16\x43hirpstack.Integrationb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,chirpstack-api/integration/integration.proto\x12\x0bintegration\x1a\"chirpstack-api/common/common.proto\x1a\x1a\x63hirpstack-api/gw/gw.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xd5\x02\n\nDeviceInfo\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x13\n\x0btenant_name\x18\x02 \x01(\t\x12\x16\n\x0e\x61pplication_id\x18\x03 \x01(\t\x12\x18\n\x10\x61pplication_name\x18\x04 \x01(\t\x12\x19\n\x11\x64\x65vice_profile_id\x18\x05 \x01(\t\x12\x1b\n\x13\x64\x65vice_profile_name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65vice_name\x18\x07 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x08 \x01(\t\x12\x31\n\x14\x64\x65vice_class_enabled\x18\n \x01(\x0e\x32\x13.common.DeviceClass\x12/\n\x04tags\x18\t \x03(\x0b\x32!.integration.DeviceInfo.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"s\n\x11UplinkRelayRxInfo\x12\x0f\n\x07\x64\x65v_eui\x18\x01 \x01(\t\x12\x11\n\tfrequency\x18\x02 \x01(\r\x12\n\n\x02\x64r\x18\x03 \x01(\r\x12\x0b\n\x03snr\x18\x04 \x01(\x05\x12\x0c\n\x04rssi\x18\x05 \x01(\x05\x12\x13\n\x0bwor_channel\x18\x06 \x01(\r\"S\n\x11JoinServerContext\x12\x16\n\x0esession_key_id\x18\x01 \x01(\t\x12&\n\tapp_s_key\x18\x02 \x01(\x0b\x32\x13.common.KeyEnvelope\"\xcd\x03\n\x0bUplinkEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x10\n\x08\x64\x65v_addr\x18\x04 \x01(\t\x12\x0b\n\x03\x61\x64r\x18\x05 \x01(\x08\x12\n\n\x02\x64r\x18\x06 \x01(\r\x12\r\n\x05\x66_cnt\x18\x07 \x01(\r\x12\x0e\n\x06\x66_port\x18\x08 \x01(\r\x12\x11\n\tconfirmed\x18\t \x01(\x08\x12\x0c\n\x04\x64\x61ta\x18\n \x01(\x0c\x12\'\n\x06object\x18\x0b \x01(\x0b\x32\x17.google.protobuf.Struct\x12!\n\x07rx_info\x18\x0c \x03(\x0b\x32\x10.gw.UplinkRxInfo\x12!\n\x07tx_info\x18\r \x01(\x0b\x32\x10.gw.UplinkTxInfo\x12\x35\n\rrelay_rx_info\x18\x0e \x01(\x0b\x32\x1e.integration.UplinkRelayRxInfo\x12;\n\x13join_server_context\x18\x0f \x01(\x0b\x32\x1e.integration.JoinServerContext\"\x83\x02\n\tJoinEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x10\n\x08\x64\x65v_addr\x18\x04 \x01(\t\x12\x35\n\rrelay_rx_info\x18\x05 \x01(\x0b\x32\x1e.integration.UplinkRelayRxInfo\x12;\n\x13join_server_context\x18\x06 \x01(\x0b\x32\x1e.integration.JoinServerContext\"\xbd\x01\n\x08\x41\x63kEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x15\n\rqueue_item_id\x18\x04 \x01(\t\x12\x14\n\x0c\x61\x63knowledged\x18\x05 \x01(\x08\x12\x12\n\nf_cnt_down\x18\x06 \x01(\r\"\xdd\x01\n\nTxAckEvent\x12\x13\n\x0b\x64ownlink_id\x18\x01 \x01(\r\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x15\n\rqueue_item_id\x18\x04 \x01(\t\x12\x12\n\nf_cnt_down\x18\x05 \x01(\r\x12\x12\n\ngateway_id\x18\x06 \x01(\t\x12#\n\x07tx_info\x18\x07 \x01(\x0b\x32\x12.gw.DownlinkTxInfo\"\xa6\x02\n\x08LogEvent\x12(\n\x04time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x02 \x01(\x0b\x32\x17.integration.DeviceInfo\x12$\n\x05level\x18\x03 \x01(\x0e\x32\x15.integration.LogLevel\x12\"\n\x04\x63ode\x18\x04 \x01(\x0e\x32\x14.integration.LogCode\x12\x13\n\x0b\x64\x65scription\x18\x05 \x01(\t\x12\x33\n\x07\x63ontext\x18\x06 \x03(\x0b\x32\".integration.LogEvent.ContextEntry\x1a.\n\x0c\x43ontextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe8\x01\n\x0bStatusEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x0e\n\x06margin\x18\x05 \x01(\x05\x12\x1d\n\x15\x65xternal_power_source\x18\x06 \x01(\x08\x12!\n\x19\x62\x61ttery_level_unavailable\x18\x07 \x01(\x08\x12\x15\n\rbattery_level\x18\x08 \x01(\x02\"\xa5\x01\n\rLocationEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\"\n\x08location\x18\x04 \x01(\x0b\x32\x10.common.Location\"\xdb\x01\n\x10IntegrationEvent\x12\x18\n\x10\x64\x65\x64uplication_id\x18\x01 \x01(\t\x12(\n\x04time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x0b\x64\x65vice_info\x18\x03 \x01(\x0b\x32\x17.integration.DeviceInfo\x12\x18\n\x10integration_name\x18\x04 \x01(\t\x12\x12\n\nevent_type\x18\x05 \x01(\t\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x88\x01\n\x0f\x44ownlinkCommand\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65v_eui\x18\x02 \x01(\t\x12\x11\n\tconfirmed\x18\x03 \x01(\x08\x12\x0e\n\x06\x66_port\x18\x04 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\'\n\x06object\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct*,\n\x08LogLevel\x12\x08\n\x04INFO\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02*\xea\x01\n\x07LogCode\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x19\n\x15\x44OWNLINK_PAYLOAD_SIZE\x10\x01\x12\x10\n\x0cUPLINK_CODEC\x10\x02\x12\x12\n\x0e\x44OWNLINK_CODEC\x10\x03\x12\x08\n\x04OTAA\x10\x04\x12\x16\n\x12UPLINK_F_CNT_RESET\x10\x05\x12\x0e\n\nUPLINK_MIC\x10\x06\x12\x1f\n\x1bUPLINK_F_CNT_RETRANSMISSION\x10\x07\x12\x14\n\x10\x44OWNLINK_GATEWAY\x10\x08\x12\x18\n\x14RELAY_NEW_END_DEVICE\x10\t\x12\x0e\n\nF_CNT_DOWN\x10\nB\x81\x01\n\x1dio.chirpstack.api.integrationB\x10IntegrationProtoP\x01Z3github.com/brocaar/chirpstack/api/go/v4/integration\xaa\x02\x16\x43hirpstack.Integrationb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -30,34 +30,36 @@ if _descriptor._USE_C_DESCRIPTORS == False: _DEVICEINFO_TAGSENTRY._serialized_options = b'8\001' _LOGEVENT_CONTEXTENTRY._options = None _LOGEVENT_CONTEXTENTRY._serialized_options = b'8\001' - _globals['_LOGLEVEL']._serialized_start=2730 - _globals['_LOGLEVEL']._serialized_end=2774 - _globals['_LOGCODE']._serialized_start=2777 - _globals['_LOGCODE']._serialized_end=2995 + _globals['_LOGLEVEL']._serialized_start=2937 + _globals['_LOGLEVEL']._serialized_end=2981 + _globals['_LOGCODE']._serialized_start=2984 + _globals['_LOGCODE']._serialized_end=3218 _globals['_DEVICEINFO']._serialized_start=189 _globals['_DEVICEINFO']._serialized_end=530 _globals['_DEVICEINFO_TAGSENTRY']._serialized_start=487 _globals['_DEVICEINFO_TAGSENTRY']._serialized_end=530 _globals['_UPLINKRELAYRXINFO']._serialized_start=532 _globals['_UPLINKRELAYRXINFO']._serialized_end=647 - _globals['_UPLINKEVENT']._serialized_start=650 - _globals['_UPLINKEVENT']._serialized_end=1050 - _globals['_JOINEVENT']._serialized_start=1053 - _globals['_JOINEVENT']._serialized_end=1251 - _globals['_ACKEVENT']._serialized_start=1254 - _globals['_ACKEVENT']._serialized_end=1443 - _globals['_TXACKEVENT']._serialized_start=1446 - _globals['_TXACKEVENT']._serialized_end=1667 - _globals['_LOGEVENT']._serialized_start=1670 - _globals['_LOGEVENT']._serialized_end=1964 - _globals['_LOGEVENT_CONTEXTENTRY']._serialized_start=1918 - _globals['_LOGEVENT_CONTEXTENTRY']._serialized_end=1964 - _globals['_STATUSEVENT']._serialized_start=1967 - _globals['_STATUSEVENT']._serialized_end=2199 - _globals['_LOCATIONEVENT']._serialized_start=2202 - _globals['_LOCATIONEVENT']._serialized_end=2367 - _globals['_INTEGRATIONEVENT']._serialized_start=2370 - _globals['_INTEGRATIONEVENT']._serialized_end=2589 - _globals['_DOWNLINKCOMMAND']._serialized_start=2592 - _globals['_DOWNLINKCOMMAND']._serialized_end=2728 + _globals['_JOINSERVERCONTEXT']._serialized_start=649 + _globals['_JOINSERVERCONTEXT']._serialized_end=732 + _globals['_UPLINKEVENT']._serialized_start=735 + _globals['_UPLINKEVENT']._serialized_end=1196 + _globals['_JOINEVENT']._serialized_start=1199 + _globals['_JOINEVENT']._serialized_end=1458 + _globals['_ACKEVENT']._serialized_start=1461 + _globals['_ACKEVENT']._serialized_end=1650 + _globals['_TXACKEVENT']._serialized_start=1653 + _globals['_TXACKEVENT']._serialized_end=1874 + _globals['_LOGEVENT']._serialized_start=1877 + _globals['_LOGEVENT']._serialized_end=2171 + _globals['_LOGEVENT_CONTEXTENTRY']._serialized_start=2125 + _globals['_LOGEVENT_CONTEXTENTRY']._serialized_end=2171 + _globals['_STATUSEVENT']._serialized_start=2174 + _globals['_STATUSEVENT']._serialized_end=2406 + _globals['_LOCATIONEVENT']._serialized_start=2409 + _globals['_LOCATIONEVENT']._serialized_end=2574 + _globals['_INTEGRATIONEVENT']._serialized_start=2577 + _globals['_INTEGRATIONEVENT']._serialized_end=2796 + _globals['_DOWNLINKCOMMAND']._serialized_start=2799 + _globals['_DOWNLINKCOMMAND']._serialized_end=2935 # @@protoc_insertion_point(module_scope) diff --git a/api/python/src/chirpstack_api/integration/integration_pb2.pyi b/api/python/src/chirpstack_api/integration/integration_pb2.pyi index 93e20468..77954bb2 100644 --- a/api/python/src/chirpstack_api/integration/integration_pb2.pyi +++ b/api/python/src/chirpstack_api/integration/integration_pb2.pyi @@ -28,6 +28,7 @@ class LogCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): UPLINK_F_CNT_RETRANSMISSION: _ClassVar[LogCode] DOWNLINK_GATEWAY: _ClassVar[LogCode] RELAY_NEW_END_DEVICE: _ClassVar[LogCode] + F_CNT_DOWN: _ClassVar[LogCode] INFO: LogLevel WARNING: LogLevel ERROR: LogLevel @@ -41,6 +42,7 @@ UPLINK_MIC: LogCode UPLINK_F_CNT_RETRANSMISSION: LogCode DOWNLINK_GATEWAY: LogCode RELAY_NEW_END_DEVICE: LogCode +F_CNT_DOWN: LogCode class DeviceInfo(_message.Message): __slots__ = ["tenant_id", "tenant_name", "application_id", "application_name", "device_profile_id", "device_profile_name", "device_name", "dev_eui", "device_class_enabled", "tags"] @@ -89,8 +91,16 @@ class UplinkRelayRxInfo(_message.Message): wor_channel: int def __init__(self, dev_eui: _Optional[str] = ..., frequency: _Optional[int] = ..., dr: _Optional[int] = ..., snr: _Optional[int] = ..., rssi: _Optional[int] = ..., wor_channel: _Optional[int] = ...) -> None: ... +class JoinServerContext(_message.Message): + __slots__ = ["session_key_id", "app_s_key"] + SESSION_KEY_ID_FIELD_NUMBER: _ClassVar[int] + APP_S_KEY_FIELD_NUMBER: _ClassVar[int] + session_key_id: str + app_s_key: _common_pb2.KeyEnvelope + def __init__(self, session_key_id: _Optional[str] = ..., app_s_key: _Optional[_Union[_common_pb2.KeyEnvelope, _Mapping]] = ...) -> None: ... + class UplinkEvent(_message.Message): - __slots__ = ["deduplication_id", "time", "device_info", "dev_addr", "adr", "dr", "f_cnt", "f_port", "confirmed", "data", "object", "rx_info", "tx_info", "relay_rx_info"] + __slots__ = ["deduplication_id", "time", "device_info", "dev_addr", "adr", "dr", "f_cnt", "f_port", "confirmed", "data", "object", "rx_info", "tx_info", "relay_rx_info", "join_server_context"] DEDUPLICATION_ID_FIELD_NUMBER: _ClassVar[int] TIME_FIELD_NUMBER: _ClassVar[int] DEVICE_INFO_FIELD_NUMBER: _ClassVar[int] @@ -105,6 +115,7 @@ class UplinkEvent(_message.Message): RX_INFO_FIELD_NUMBER: _ClassVar[int] TX_INFO_FIELD_NUMBER: _ClassVar[int] RELAY_RX_INFO_FIELD_NUMBER: _ClassVar[int] + JOIN_SERVER_CONTEXT_FIELD_NUMBER: _ClassVar[int] deduplication_id: str time: _timestamp_pb2.Timestamp device_info: DeviceInfo @@ -119,21 +130,24 @@ class UplinkEvent(_message.Message): rx_info: _containers.RepeatedCompositeFieldContainer[_gw_pb2.UplinkRxInfo] tx_info: _gw_pb2.UplinkTxInfo relay_rx_info: UplinkRelayRxInfo - def __init__(self, deduplication_id: _Optional[str] = ..., time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., device_info: _Optional[_Union[DeviceInfo, _Mapping]] = ..., dev_addr: _Optional[str] = ..., adr: bool = ..., dr: _Optional[int] = ..., f_cnt: _Optional[int] = ..., f_port: _Optional[int] = ..., confirmed: bool = ..., data: _Optional[bytes] = ..., object: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., rx_info: _Optional[_Iterable[_Union[_gw_pb2.UplinkRxInfo, _Mapping]]] = ..., tx_info: _Optional[_Union[_gw_pb2.UplinkTxInfo, _Mapping]] = ..., relay_rx_info: _Optional[_Union[UplinkRelayRxInfo, _Mapping]] = ...) -> None: ... + join_server_context: JoinServerContext + def __init__(self, deduplication_id: _Optional[str] = ..., time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., device_info: _Optional[_Union[DeviceInfo, _Mapping]] = ..., dev_addr: _Optional[str] = ..., adr: bool = ..., dr: _Optional[int] = ..., f_cnt: _Optional[int] = ..., f_port: _Optional[int] = ..., confirmed: bool = ..., data: _Optional[bytes] = ..., object: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., rx_info: _Optional[_Iterable[_Union[_gw_pb2.UplinkRxInfo, _Mapping]]] = ..., tx_info: _Optional[_Union[_gw_pb2.UplinkTxInfo, _Mapping]] = ..., relay_rx_info: _Optional[_Union[UplinkRelayRxInfo, _Mapping]] = ..., join_server_context: _Optional[_Union[JoinServerContext, _Mapping]] = ...) -> None: ... class JoinEvent(_message.Message): - __slots__ = ["deduplication_id", "time", "device_info", "dev_addr", "relay_rx_info"] + __slots__ = ["deduplication_id", "time", "device_info", "dev_addr", "relay_rx_info", "join_server_context"] DEDUPLICATION_ID_FIELD_NUMBER: _ClassVar[int] TIME_FIELD_NUMBER: _ClassVar[int] DEVICE_INFO_FIELD_NUMBER: _ClassVar[int] DEV_ADDR_FIELD_NUMBER: _ClassVar[int] RELAY_RX_INFO_FIELD_NUMBER: _ClassVar[int] + JOIN_SERVER_CONTEXT_FIELD_NUMBER: _ClassVar[int] deduplication_id: str time: _timestamp_pb2.Timestamp device_info: DeviceInfo dev_addr: str relay_rx_info: UplinkRelayRxInfo - def __init__(self, deduplication_id: _Optional[str] = ..., time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., device_info: _Optional[_Union[DeviceInfo, _Mapping]] = ..., dev_addr: _Optional[str] = ..., relay_rx_info: _Optional[_Union[UplinkRelayRxInfo, _Mapping]] = ...) -> None: ... + join_server_context: JoinServerContext + def __init__(self, deduplication_id: _Optional[str] = ..., time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., device_info: _Optional[_Union[DeviceInfo, _Mapping]] = ..., dev_addr: _Optional[str] = ..., relay_rx_info: _Optional[_Union[UplinkRelayRxInfo, _Mapping]] = ..., join_server_context: _Optional[_Union[JoinServerContext, _Mapping]] = ...) -> None: ... class AckEvent(_message.Message): __slots__ = ["deduplication_id", "time", "device_info", "queue_item_id", "acknowledged", "f_cnt_down"] diff --git a/api/rust/proto/chirpstack/api/device.proto b/api/rust/proto/chirpstack/api/device.proto index bdd8796f..0d739a0c 100644 --- a/api/rust/proto/chirpstack/api/device.proto +++ b/api/rust/proto/chirpstack/api/device.proto @@ -165,6 +165,17 @@ service DeviceService { get : "/api/devices/{dev_eui}/queue" }; } + + // GetNextFCntDown returns the next FCntDown to use for enqueing encrypted + // downlinks. The difference with the DeviceActivation f_cont_down is that + // this method takes potential existing queue-items into account. + rpc GetNextFCntDown(GetDeviceNextFCntDownRequest) + returns (GetDeviceNextFCntDownResponse) { + option (google.api.http) = { + post : "/api/devices/{dev_eui}/get-next-f-cnt-down" + body : "*" + }; + } } message Device { @@ -504,12 +515,20 @@ message DeviceQueueItem { google.protobuf.Struct object = 6; // Is pending. - // This is set to true when the downlink is pending. + // This is set by ChirpStack to true when the downlink is pending (e.g. it + // has been sent, but a confirmation is still pending). bool is_pending = 7; // Downlink frame-counter. - // This is set when the payload has been sent as downlink. + // Do not set this for plain-text data payloads. It will be automatically set + // by ChirpStack when the payload has been sent as downlink. uint32 f_cnt_down = 8; + + // Is encrypted. + // This must be set to true if the end-application has already encrypted + // the data payload. In this case, the f_cnt_down field must be set to + // the corresponding frame-counter which has been used during the encryption. + bool is_encrypted = 9; } message EnqueueDeviceQueueItemRequest { DeviceQueueItem queue_item = 1; } @@ -544,3 +563,13 @@ message FlushDevNoncesRequest { // Device EUI (EUI64). string dev_eui = 1; } + +message GetDeviceNextFCntDownRequest { + // Device EUI (EUI64). + string dev_eui = 1; +} + +message GetDeviceNextFCntDownResponse { + // FCntDown. + uint32 f_cnt_down = 1; +} \ No newline at end of file diff --git a/api/rust/proto/chirpstack/integration/integration.proto b/api/rust/proto/chirpstack/integration/integration.proto index 93ddc020..552ddb2f 100644 --- a/api/rust/proto/chirpstack/integration/integration.proto +++ b/api/rust/proto/chirpstack/integration/integration.proto @@ -55,6 +55,9 @@ enum LogCode { // Relay new end-device. RELAY_NEW_END_DEVICE = 9; + + // Downlink frame-counter. + F_CNT_DOWN = 10; } // Device information. @@ -111,6 +114,15 @@ message UplinkRelayRxInfo { uint32 wor_channel = 6; } +// Join-Server context. +message JoinServerContext { + // Session-key ID. + string session_key_id = 1; + + // AppSKey envelope. + common.KeyEnvelope app_s_key = 2; +} + // UplinkEvent is the message sent when an uplink payload has been received. message UplinkEvent { // Deduplication ID (UUID). @@ -155,6 +167,12 @@ message UplinkEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 14; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 15; } // JoinEvent is the message sent when a device joined the network. @@ -174,6 +192,12 @@ message JoinEvent { // Relay info. UplinkRelayRxInfo relay_rx_info = 5; + + // Join-Server context. + // A non-empty value indicatest that ChirpStack does not have access to + // the AppSKey and that the encryption / decryption of the payloads is + // the responsibility of the end-application. + JoinServerContext join_server_context = 6; } // AckEvent is the message sent when a confirmation on a confirmed downlink diff --git a/api/rust/proto/chirpstack/internal/internal.proto b/api/rust/proto/chirpstack/internal/internal.proto index d35f000b..e3483e1b 100644 --- a/api/rust/proto/chirpstack/internal/internal.proto +++ b/api/rust/proto/chirpstack/internal/internal.proto @@ -31,6 +31,9 @@ message DeviceSession { // AppSKey envelope. common.KeyEnvelope app_s_key = 8; + // JS Session Key ID. + bytes js_session_key_id = 42; + // Uplink frame-counter. uint32 f_cnt_up = 9; diff --git a/api/rust/src/integration.rs b/api/rust/src/integration.rs index 19115ce5..287f348c 100644 --- a/api/rust/src/integration.rs +++ b/api/rust/src/integration.rs @@ -31,6 +31,7 @@ impl Into for LogCode { LogCode::UplinkFCntRetransmission => "UPLINK_F_CNT_RETRANSMISSION", LogCode::DownlinkGateway => "DOWNLINK_GATEWAY", LogCode::RelayNewEndDevice => "RELAY_NEW_END_DEVICE", + LogCode::FCntDown => "F_CNT_DOWN", } .to_string() } diff --git a/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/down.sql b/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/down.sql new file mode 100644 index 00000000..df2f324a --- /dev/null +++ b/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/down.sql @@ -0,0 +1,2 @@ +alter table device_queue_item + drop column is_encrypted; diff --git a/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/up.sql b/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/up.sql new file mode 100644 index 00000000..1eb9b5a0 --- /dev/null +++ b/chirpstack/migrations/2023-09-25-105457_encrypted_queue_items/up.sql @@ -0,0 +1,5 @@ +alter table device_queue_item + add column is_encrypted boolean default false not null; + +alter table device_queue_item + alter column is_encrypted drop default; diff --git a/chirpstack/src/api/device.rs b/chirpstack/src/api/device.rs index ca0fd1f2..d78685cc 100644 --- a/chirpstack/src/api/device.rs +++ b/chirpstack/src/api/device.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::collections::HashSet; use std::str::FromStr; use std::time::SystemTime; @@ -1040,6 +1041,12 @@ impl DeviceService for Device { dev_eui, f_port: req_qi.f_port as i16, confirmed: req_qi.confirmed, + is_encrypted: req_qi.is_encrypted, + f_cnt_down: if req_qi.is_encrypted { + Some(req_qi.f_cnt_down.into()) + } else { + None + }, data, ..Default::default() }; @@ -1112,10 +1119,8 @@ impl DeviceService for Device { data: qi.data.clone(), object: None, is_pending: qi.is_pending, - f_cnt_down: match qi.f_cnt_down { - None => 0, - Some(v) => v as u32, - }, + f_cnt_down: qi.f_cnt_down.unwrap_or(0) as u32, + is_encrypted: qi.is_encrypted, }) .collect(), }); @@ -1124,6 +1129,36 @@ impl DeviceService for Device { Ok(resp) } + + async fn get_next_f_cnt_down( + &self, + request: Request, + ) -> Result, Status> { + let req = request.get_ref(); + let dev_eui = EUI64::from_str(&req.dev_eui).map_err(|e| e.status())?; + + self.validator + .validate( + request.extensions(), + validator::ValidateDeviceAccess::new(validator::Flag::Read, dev_eui), + ) + .await?; + + let ds = device_session::get(&dev_eui).await.unwrap_or_default(); + + let max_f_cnt_down_queue = device_queue::get_max_f_cnt_down(dev_eui) + .await + .map_err(|e| e.status())? + .unwrap_or_default() as u32; + + let mut resp = Response::new(api::GetDeviceNextFCntDownResponse { + f_cnt_down: cmp::max(ds.get_a_f_cnt_down(), max_f_cnt_down_queue + 1), + }); + resp.metadata_mut() + .insert("x-log-dev_eui", req.dev_eui.parse().unwrap()); + + Ok(resp) + } } #[cfg(test)] @@ -1407,6 +1442,19 @@ pub mod test { get_activation_resp.get_ref().device_activation ); + // get next FCntDown (from device-session) + let get_next_f_cnt_req = get_request( + &u.id, + api::GetDeviceNextFCntDownRequest { + dev_eui: "0102030405060708".into(), + }, + ); + let get_next_f_cnt_resp = service + .get_next_f_cnt_down(get_next_f_cnt_req) + .await + .unwrap(); + assert_eq!(1, get_next_f_cnt_resp.get_ref().f_cnt_down); + // deactivate let deactivate_req = get_request( &u.id, @@ -1455,6 +1503,22 @@ pub mod test { ); let _ = service.enqueue(enqueue_req).await.unwrap(); + let enqueue_req = get_request( + &u.id, + api::EnqueueDeviceQueueItemRequest { + queue_item: Some(api::DeviceQueueItem { + dev_eui: "0102030405060708".into(), + confirmed: true, + f_port: 2, + f_cnt_down: 10, + data: vec![1, 2, 3], + is_encrypted: true, + ..Default::default() + }), + }, + ); + let _ = service.enqueue(enqueue_req).await.unwrap(); + // get queue let get_queue_req = get_request( &u.id, @@ -1465,9 +1529,27 @@ pub mod test { ); let get_queue_resp = service.get_queue(get_queue_req).await.unwrap(); let get_queue_resp = get_queue_resp.get_ref(); - assert_eq!(1, get_queue_resp.total_count); - assert_eq!(1, get_queue_resp.result.len()); + assert_eq!(2, get_queue_resp.total_count); + assert_eq!(2, get_queue_resp.result.len()); assert_eq!(vec![3, 2, 1], get_queue_resp.result[0].data); + assert_eq!(false, get_queue_resp.result[0].is_encrypted); + assert_eq!(0, get_queue_resp.result[0].f_cnt_down); + assert_eq!(vec![1, 2, 3], get_queue_resp.result[1].data); + assert_eq!(true, get_queue_resp.result[1].is_encrypted); + assert_eq!(10, get_queue_resp.result[1].f_cnt_down); + + // get next FCntDown (from queue) + let get_next_f_cnt_req = get_request( + &u.id, + api::GetDeviceNextFCntDownRequest { + dev_eui: "0102030405060708".into(), + }, + ); + let get_next_f_cnt_resp = service + .get_next_f_cnt_down(get_next_f_cnt_req) + .await + .unwrap(); + assert_eq!(11, get_next_f_cnt_resp.get_ref().f_cnt_down); // flush queue let flush_queue_req = get_request( diff --git a/chirpstack/src/downlink/data.rs b/chirpstack/src/downlink/data.rs index 9baf3c13..9dafe2da 100644 --- a/chirpstack/src/downlink/data.rs +++ b/chirpstack/src/downlink/data.rs @@ -413,9 +413,16 @@ impl Data { }, }; - // The queue item should fit within the max payload size and should not be pending - // already. - if qi.data.len() <= max_payload_size && !qi.is_pending { + // The queue item: + // * should fit within the max payload size + // * should not be pending + // * in case encrypted, should have a valid FCntDown + if qi.data.len() <= max_payload_size + && !qi.is_pending + && !(qi.is_encrypted + && (qi.f_cnt_down.unwrap_or_default() as u32) + < self.device_session.get_a_f_cnt_down()) + { trace!(id = %qi.id, more_in_queue = more_in_queue, "Found device queue-item for downlink"); self.device_queue_item = Some(qi); self.more_device_queue_items = more_in_queue; @@ -499,6 +506,44 @@ impl Data { continue; } + + // Handling encrypted payload with invalid FCntDown + if qi.is_encrypted + && (qi.f_cnt_down.unwrap_or_default() as u32) + < self.device_session.get_a_f_cnt_down() + { + device_queue::delete_item(&qi.id) + .await + .context("Delete device queue-item")?; + + let pl = integration_pb::LogEvent { + time: Some(Utc::now().into()), + device_info: Some(device_info.clone()), + level: integration_pb::LogLevel::Error.into(), + code: integration_pb::LogCode::FCntDown.into(), + description: "Device queue-item discarded because the frame-counter is invalid" + .to_string(), + context: [ + ( + "device_f_cnt_down".to_string(), + self.device_session.get_a_f_cnt_down().to_string(), + ), + ( + "queue_item_f_cnt_down".to_string(), + qi.f_cnt_down.unwrap_or_default().to_string(), + ), + ("queue_item_id".to_string(), qi.id.to_string()), + ] + .iter() + .cloned() + .collect(), + }; + + integration::log_event(self.application.id, &self.device.variables, &pl).await; + warn!(dev_eui = %self.device.dev_eui, device_queue_item_id = %qi.id, "Device queue-item discarded because of invalid frame-counter"); + + continue; + } } } @@ -650,7 +695,10 @@ impl Data { if qi.data.len() <= item.remaining_payload_size { // Set the device-queue item. mac_pl.f_port = Some(qi.f_port as u8); - mac_pl.fhdr.f_cnt = self.device_session.get_a_f_cnt_down(); + mac_pl.fhdr.f_cnt = match qi.is_encrypted { + true => qi.f_cnt_down.unwrap_or_default() as u32, + false => self.device_session.get_a_f_cnt_down(), + }; mac_pl.frm_payload = Some(lrwn::FRMPayload::Raw(qi.data.clone())); if qi.confirmed { @@ -673,13 +721,18 @@ impl Data { }; // Encrypt FRMPayload. + let qi_encrypted = match &self.device_queue_item { + Some(v) => v.is_encrypted, + None => false, + }; + if mac_size > 15 { // Encrypt mac-commands. phy.encrypt_frm_payload(&lrwn::AES128Key::from_slice( &self.device_session.nwk_s_enc_key, )?) .context("Encrypt frm_payload mac-commands")?; - } else { + } else if self.device_queue_item.is_some() && !qi_encrypted { // Encrypt application payload. if let Some(key_env) = &self.device_session.app_s_key { let app_s_key = lrwn::AES128Key::from_slice(&key_env.aes_key)?; @@ -781,18 +834,12 @@ impl Data { // * get_next_device_queue_item is called on next downlink // * as is_pending was already set to true, a negative ack event is sent // and item is popped from the queue - qi.f_cnt_down = Some(if self - .device_session - .mac_version() - .to_string() - .starts_with("1.0") - { - self.device_session.n_f_cnt_down - } else { - self.device_session.a_f_cnt_down - } as i64); - *qi = device_queue::update_item(qi.clone()).await?; + // Do not update the frame-counter in case the queue-item is encrypted. + if !qi.is_encrypted { + qi.f_cnt_down = Some(self.device_session.get_a_f_cnt_down() as i64); + *qi = device_queue::update_item(qi.clone()).await?; + } } Ok(()) @@ -816,7 +863,13 @@ impl Data { nwk_s_enc_key: self.device_session.nwk_s_enc_key.clone(), downlink_frame: Some(self.downlink_frame.clone()), n_f_cnt_down: self.device_session.n_f_cnt_down, - a_f_cnt_down: self.device_session.get_a_f_cnt_down(), + a_f_cnt_down: match &self.device_queue_item { + Some(v) => match v.is_encrypted { + true => v.f_cnt_down.unwrap_or_default() as u32, + false => self.device_session.get_a_f_cnt_down(), + }, + None => self.device_session.get_a_f_cnt_down(), + }, ..Default::default() }) .await @@ -2476,6 +2529,261 @@ mod test { use super::*; use crate::test; use lrwn::EUI64; + use tokio::time::sleep; + use uuid::Uuid; + + #[tokio::test] + async fn test_get_next_device_queue_item() { + let _guard = test::prepare().await; + + let t = tenant::create(tenant::Tenant { + name: "test-tenant".into(), + ..Default::default() + }) + .await + .unwrap(); + + let dp = device_profile::create(device_profile::DeviceProfile { + name: "dp".into(), + tenant_id: t.id, + is_relay: true, + ..Default::default() + }) + .await + .unwrap(); + + let app = application::create(application::Application { + name: "test-app".into(), + tenant_id: t.id, + ..Default::default() + }) + .await + .unwrap(); + + let d = device::create(device::Device { + dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]), + name: "dev".into(), + application_id: app.id, + device_profile_id: dp.id, + ..Default::default() + }) + .await + .unwrap(); + + let ds = internal::DeviceSession { + n_f_cnt_down: 10, + a_f_cnt_down: 10, + ..Default::default() + }; + + struct Test { + name: String, + max_payload_size: usize, + queue_items: Vec, + expected_queue_item: Option, + expected_ack_event: Option, + expected_log_event: Option, + } + + let qi_id = Uuid::new_v4(); + + let tests = vec![ + Test { + name: "max payload size error".into(), + max_payload_size: 10, + queue_items: vec![device_queue::DeviceQueueItem { + id: qi_id, + dev_eui: d.dev_eui, + f_port: 1, + data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + ..Default::default() + }], + expected_queue_item: None, + expected_ack_event: None, + expected_log_event: Some(integration_pb::LogEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: d.name.clone(), + dev_eui: d.dev_eui.to_string(), + ..Default::default() + }), + level: integration_pb::LogLevel::Error.into(), + code: integration_pb::LogCode::DownlinkPayloadSize.into(), + description: + "Device queue-item discarded because it exceeds the max. payload size" + .into(), + context: [ + ("item_size".to_string(), "11".to_string()), + ("queue_item_id".to_string(), qi_id.to_string()), + ("max_payload_size".to_string(), "10".to_string()), + ] + .iter() + .cloned() + .collect(), + ..Default::default() + }), + }, + Test { + name: "is pending".into(), + max_payload_size: 10, + queue_items: vec![device_queue::DeviceQueueItem { + id: qi_id, + dev_eui: d.dev_eui, + f_port: 1, + f_cnt_down: Some(10), + data: vec![1, 2, 3], + is_pending: true, + ..Default::default() + }], + expected_queue_item: None, + expected_log_event: None, + expected_ack_event: Some(integration_pb::AckEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: d.name.clone(), + dev_eui: d.dev_eui.to_string(), + ..Default::default() + }), + queue_item_id: qi_id.to_string(), + acknowledged: false, + f_cnt_down: 10, + ..Default::default() + }), + }, + Test { + name: "invalid frame-counter".into(), + max_payload_size: 10, + queue_items: vec![device_queue::DeviceQueueItem { + id: qi_id, + dev_eui: d.dev_eui, + f_port: 1, + data: vec![1, 2, 3], + f_cnt_down: Some(5), + is_encrypted: true, + ..Default::default() + }], + expected_queue_item: None, + expected_ack_event: None, + expected_log_event: Some(integration_pb::LogEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: d.name.clone(), + dev_eui: d.dev_eui.to_string(), + ..Default::default() + }), + level: integration_pb::LogLevel::Error.into(), + code: integration_pb::LogCode::FCntDown.into(), + description: "Device queue-item discarded because the frame-counter is invalid" + .into(), + context: [ + ("queue_item_id".to_string(), qi_id.to_string()), + ("device_f_cnt_down".to_string(), "10".to_string()), + ("queue_item_f_cnt_down".to_string(), "5".to_string()), + ] + .iter() + .cloned() + .collect(), + ..Default::default() + }), + }, + Test { + name: "valid payload".into(), + max_payload_size: 10, + queue_items: vec![device_queue::DeviceQueueItem { + id: qi_id, + dev_eui: d.dev_eui, + f_port: 1, + data: vec![1, 2, 3], + ..Default::default() + }], + expected_queue_item: Some(device_queue::DeviceQueueItem { + id: qi_id, + dev_eui: d.dev_eui, + f_port: 1, + data: vec![1, 2, 3], + ..Default::default() + }), + expected_log_event: None, + expected_ack_event: None, + }, + ]; + + for tst in &tests { + println!("> {}", tst.name); + + integration::set_mock().await; + integration::mock::reset().await; + + device_queue::flush_for_dev_eui(&d.dev_eui).await.unwrap(); + for qi in &tst.queue_items { + device_queue::enqueue_item(qi.clone()).await.unwrap(); + } + + let mut ctx = Data { + relay_context: None, + uplink_frame_set: None, + tenant: t.clone(), + application: app.clone(), + device_profile: dp.clone(), + device: d.clone(), + device_session: ds.clone(), + network_conf: config::get_region_network("eu868").unwrap(), + region_conf: region::get("eu868").unwrap(), + must_send: false, + must_ack: false, + mac_commands: vec![], + device_gateway_rx_info: None, + downlink_gateway: None, + downlink_frame: Default::default(), + downlink_frame_items: vec![DownlinkFrameItem { + downlink_frame_item: Default::default(), + remaining_payload_size: tst.max_payload_size, + }], + immediately: false, + device_queue_item: None, + more_device_queue_items: false, + }; + + ctx.get_next_device_queue_item().await.unwrap(); + + // Integrations are handled async. + sleep(Duration::from_millis(100)).await; + + if let Some(log) = &tst.expected_log_event { + let mut event = integration::mock::get_log_event().await.unwrap(); + assert_ne!(None, event.time); + event.time = None; + assert_eq!(log, &event); + } + + if let Some(ack) = &tst.expected_ack_event { + let mut event = integration::mock::get_ack_event().await.unwrap(); + assert_ne!(None, event.time); + event.time = None; + assert_eq!(ack, &event); + } + + if let Some(qi) = &tst.expected_queue_item { + assert_ne!(None, ctx.device_queue_item); + assert_eq!(qi.id, ctx.device_queue_item.as_ref().unwrap().id); + } + } + } #[test] fn test_filter_mac_commands() { diff --git a/chirpstack/src/downlink/tx_ack.rs b/chirpstack/src/downlink/tx_ack.rs index 7f4ee0af..9bbb06c1 100644 --- a/chirpstack/src/downlink/tx_ack.rs +++ b/chirpstack/src/downlink/tx_ack.rs @@ -389,7 +389,7 @@ impl TxAck { trace!("Incrementing a_f_cnt_down"); let ds = self.device_session.as_mut().unwrap(); - ds.set_a_f_cnt_down(ds.get_a_f_cnt_down() + 1); + ds.set_a_f_cnt_down(self.downlink_frame.as_ref().unwrap().a_f_cnt_down + 1); Ok(()) } diff --git a/chirpstack/src/integration/mock.rs b/chirpstack/src/integration/mock.rs index b4ac44b8..3cc70cbe 100644 --- a/chirpstack/src/integration/mock.rs +++ b/chirpstack/src/integration/mock.rs @@ -108,6 +108,20 @@ impl IntegrationTrait for Integration { } } +pub async fn get_join_event() -> Option { + if JOIN_EVENTS.read().await.is_empty() { + return None; + } + + JOIN_EVENTS + .write() + .await + .drain(0..1) + .collect::>() + .first() + .cloned() +} + pub async fn get_uplink_event() -> Option { if UPLINK_EVENTS.read().await.is_empty() { return None; @@ -122,6 +136,34 @@ pub async fn get_uplink_event() -> Option { .cloned() } +pub async fn get_ack_event() -> Option { + if ACK_EVENTS.read().await.is_empty() { + return None; + } + + ACK_EVENTS + .write() + .await + .drain(0..1) + .collect::>() + .first() + .cloned() +} + +pub async fn get_log_event() -> Option { + if LOG_EVENTS.read().await.is_empty() { + return None; + } + + LOG_EVENTS + .write() + .await + .drain(0..1) + .collect::>() + .first() + .cloned() +} + pub async fn get_join_events() -> Vec { JOIN_EVENTS.write().await.drain(..).collect() } diff --git a/chirpstack/src/storage/device_queue.rs b/chirpstack/src/storage/device_queue.rs index e8510760..f1074c9e 100644 --- a/chirpstack/src/storage/device_queue.rs +++ b/chirpstack/src/storage/device_queue.rs @@ -1,6 +1,6 @@ use anyhow::Result; use chrono::{DateTime, Utc}; -use diesel::prelude::*; +use diesel::{dsl, prelude::*}; use tokio::task; use tracing::info; use uuid::Uuid; @@ -22,6 +22,7 @@ pub struct DeviceQueueItem { pub is_pending: bool, pub f_cnt_down: Option, pub timeout_after: Option>, + pub is_encrypted: bool, } impl DeviceQueueItem { @@ -32,6 +33,12 @@ impl DeviceQueueItem { )); } + if self.is_encrypted && self.f_cnt_down.is_none() { + return Err(Error::Validation( + "FCntDown must be set for encrypted queue-items".to_string(), + )); + } + Ok(()) } } @@ -50,6 +57,7 @@ impl Default for DeviceQueueItem { is_pending: false, f_cnt_down: None, timeout_after: None, + is_encrypted: false, } } } @@ -211,6 +219,19 @@ pub async fn get_pending_for_dev_eui(dev_eui: &EUI64) -> Result Result, Error> { + task::spawn_blocking({ + move || -> Result, Error> { + let mut c = get_db_conn()?; + Ok(device_queue_item::dsl::device_queue_item + .select(dsl::max(device_queue_item::f_cnt_down)) + .filter(device_queue_item::dsl::dev_eui.eq(dev_eui)) + .first(&mut c)?) + } + }) + .await? +} + #[cfg(test)] pub mod test { use super::*; @@ -302,4 +323,34 @@ pub mod test { flush_for_dev_eui(&d.dev_eui).await.unwrap(); assert_eq!(true, delete_item(&qi.id).await.is_err()); } + + #[tokio::test] + async fn test_get_max_f_cnt_down() { + let _guard = test::prepare().await; + let dp = storage::device_profile::test::create_device_profile(None).await; + let d = storage::device::test::create_device( + EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + dp.id, + None, + ) + .await; + + // create + let mut qi = DeviceQueueItem { + dev_eui: d.dev_eui, + f_port: 10, + data: vec![0x01, 0x02, 0x03], + ..Default::default() + }; + qi = enqueue_item(qi).await.unwrap(); + + // No max_f_cnt. + let max_f_cnt = get_max_f_cnt_down(d.dev_eui).await.unwrap(); + assert_eq!(None, max_f_cnt); + + qi.f_cnt_down = Some(10); + update_item(qi).await.unwrap(); + let max_f_cnt = get_max_f_cnt_down(d.dev_eui).await.unwrap(); + assert_eq!(Some(10), max_f_cnt); + } } diff --git a/chirpstack/src/storage/schema.rs b/chirpstack/src/storage/schema.rs index b80df3f6..32595b69 100644 --- a/chirpstack/src/storage/schema.rs +++ b/chirpstack/src/storage/schema.rs @@ -4,6 +4,7 @@ diesel::table! { api_key (id) { id -> Uuid, created_at -> Timestamptz, + #[max_length = 100] name -> Varchar, is_admin -> Bool, tenant_id -> Nullable, @@ -16,6 +17,7 @@ diesel::table! { tenant_id -> Uuid, created_at -> Timestamptz, updated_at -> Timestamptz, + #[max_length = 100] name -> Varchar, description -> Text, mqtt_tls_cert -> Nullable, @@ -25,6 +27,7 @@ diesel::table! { diesel::table! { application_integration (application_id, kind) { application_id -> Uuid, + #[max_length = 20] kind -> Varchar, created_at -> Timestamptz, updated_at -> Timestamptz, @@ -41,6 +44,7 @@ diesel::table! { updated_at -> Timestamptz, last_seen_at -> Nullable, scheduler_run_after -> Nullable, + #[max_length = 100] name -> Varchar, description -> Text, external_power_source -> Bool, @@ -51,6 +55,7 @@ diesel::table! { longitude -> Nullable, altitude -> Nullable, dev_addr -> Nullable, + #[max_length = 1] enabled_class -> Bpchar, skip_fcnt_check -> Bool, is_disabled -> Bool, @@ -78,11 +83,17 @@ diesel::table! { tenant_id -> Uuid, created_at -> Timestamptz, updated_at -> Timestamptz, + #[max_length = 100] name -> Varchar, + #[max_length = 10] region -> Varchar, + #[max_length = 10] mac_version -> Varchar, + #[max_length = 20] reg_params_revision -> Varchar, + #[max_length = 100] adr_algorithm_id -> Varchar, + #[max_length = 20] payload_codec_runtime -> Varchar, uplink_interval -> Int4, device_status_req_interval -> Int4, @@ -104,6 +115,7 @@ diesel::table! { description -> Text, measurements -> Jsonb, auto_detect_measurements -> Bool, + #[max_length = 100] region_config_id -> Nullable, is_relay -> Bool, is_relay_ed -> Bool, @@ -135,14 +147,22 @@ diesel::table! { id -> Text, created_at -> Timestamptz, updated_at -> Timestamptz, + #[max_length = 100] name -> Varchar, description -> Text, + #[max_length = 100] vendor -> Varchar, + #[max_length = 100] firmware -> Varchar, + #[max_length = 10] region -> Varchar, + #[max_length = 10] mac_version -> Varchar, + #[max_length = 20] reg_params_revision -> Varchar, + #[max_length = 100] adr_algorithm_id -> Varchar, + #[max_length = 20] payload_codec_runtime -> Varchar, payload_codec_script -> Text, uplink_interval -> Int4, @@ -177,6 +197,7 @@ diesel::table! { is_pending -> Bool, f_cnt_down -> Nullable, timeout_after -> Nullable, + is_encrypted -> Bool, } } @@ -187,6 +208,7 @@ diesel::table! { created_at -> Timestamptz, updated_at -> Timestamptz, last_seen_at -> Nullable, + #[max_length = 100] name -> Varchar, description -> Text, latitude -> Float8, @@ -205,16 +227,20 @@ diesel::table! { application_id -> Uuid, created_at -> Timestamptz, updated_at -> Timestamptz, + #[max_length = 100] name -> Varchar, + #[max_length = 10] region -> Varchar, mc_addr -> Bytea, mc_nwk_s_key -> Bytea, mc_app_s_key -> Bytea, f_cnt -> Int8, + #[max_length = 1] group_type -> Bpchar, dr -> Int2, frequency -> Int8, class_b_ping_slot_period -> Int4, + #[max_length = 20] class_c_scheduling_type -> Varchar, } } @@ -262,6 +288,7 @@ diesel::table! { id -> Uuid, created_at -> Timestamptz, updated_at -> Timestamptz, + #[max_length = 100] name -> Varchar, description -> Text, can_have_gateways -> Bool, @@ -294,6 +321,7 @@ diesel::table! { is_active -> Bool, email -> Text, email_verified -> Bool, + #[max_length = 200] password_hash -> Varchar, note -> Text, } diff --git a/chirpstack/src/test/assert.rs b/chirpstack/src/test/assert.rs index 43feaadf..51768748 100644 --- a/chirpstack/src/test/assert.rs +++ b/chirpstack/src/test/assert.rs @@ -149,6 +149,25 @@ pub fn no_uplink_event() -> Validator { }) } +pub fn join_event(join: integration_pb::JoinEvent) -> Validator { + Box::new(move || { + let join = join.clone(); + Box::pin(async move { + // Integration events are handled async. + sleep(Duration::from_millis(100)).await; + + let mut event = mock::get_join_event().await.unwrap(); + + assert_ne!("", event.deduplication_id); + assert_ne!(None, event.time); + + event.deduplication_id = "".into(); + event.time = None; + assert_eq!(join, event); + }) + }) +} + pub fn uplink_event(up: integration_pb::UplinkEvent) -> Validator { Box::new(move || { let up = up.clone(); diff --git a/chirpstack/src/test/class_a_test.rs b/chirpstack/src/test/class_a_test.rs index 2145c326..d84c2cae 100644 --- a/chirpstack/src/test/class_a_test.rs +++ b/chirpstack/src/test/class_a_test.rs @@ -1653,6 +1653,334 @@ async fn test_lorawan_10_uplink() { } } +#[tokio::test] +async fn test_lorawan_10_end_to_end_enc() { + let _guard = test::prepare().await; + + let t = tenant::create(tenant::Tenant { + name: "tenant".into(), + can_have_gateways: true, + ..Default::default() + }) + .await + .unwrap(); + + let gw = gateway::create(gateway::Gateway { + name: "gateway".into(), + tenant_id: t.id.clone(), + gateway_id: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + ..Default::default() + }) + .await + .unwrap(); + + let app = application::create(application::Application { + name: "app".into(), + tenant_id: t.id.clone(), + ..Default::default() + }) + .await + .unwrap(); + + let dp = device_profile::create(device_profile::DeviceProfile { + name: "dp".into(), + tenant_id: t.id.clone(), + region: lrwn::region::CommonName::EU868, + mac_version: lrwn::region::MacVersion::LORAWAN_1_0_4, + reg_params_revision: lrwn::region::Revision::RP002_1_0_3, + supports_otaa: true, + ..Default::default() + }) + .await + .unwrap(); + + let dev = device::create(device::Device { + name: "device".into(), + application_id: app.id.clone(), + device_profile_id: dp.id.clone(), + dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]), + enabled_class: DeviceClass::A, + ..Default::default() + }) + .await + .unwrap(); + + let mut rx_info = gw::UplinkRxInfo { + gateway_id: gw.gateway_id.to_string(), + location: Some(Default::default()), + ..Default::default() + }; + rx_info + .metadata + .insert("region_config_id".to_string(), "eu868".to_string()); + rx_info + .metadata + .insert("region_common_name".to_string(), "EU868".to_string()); + + let mut tx_info = gw::UplinkTxInfo { + frequency: 868100000, + ..Default::default() + }; + uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap(); + + let ds_sess_key_id = internal::DeviceSession { + dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8], + mac_version: common::MacVersion::Lorawan104.into(), + join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1], + dev_addr: vec![1, 2, 3, 4], + f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + app_s_key: None, + js_session_key_id: vec![1, 2, 3], + f_cnt_up: 8, + n_f_cnt_down: 5, + enabled_uplink_channel_indices: vec![0, 1, 2], + rx1_delay: 1, + rx2_frequency: 869525000, + region_config_id: "eu868".into(), + ..Default::default() + }; + + let ds_app_s_key = internal::DeviceSession { + dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8], + mac_version: common::MacVersion::Lorawan104.into(), + join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1], + dev_addr: vec![1, 2, 3, 4], + f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + app_s_key: Some(common::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3], + }), + f_cnt_up: 8, + n_f_cnt_down: 5, + enabled_uplink_channel_indices: vec![0, 1, 2], + rx1_delay: 1, + rx2_frequency: 869525000, + region_config_id: "eu868".into(), + ..Default::default() + }; + + let tests = vec![ + Test { + name: "end-to-end encryption with session key id".into(), + device_queue_items: vec![], + before_func: None, + after_func: None, + device_session: Some(ds_sess_key_id.clone()), + tx_info: tx_info.clone(), + rx_info: rx_info.clone(), + phy_payload: lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataUp, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]), + f_cnt: 10, + ..Default::default() + }, + f_port: Some(1), + frm_payload: Some(lrwn::FRMPayload::Raw(vec![1, 2, 3, 4])), + }), + mic: Some([104, 147, 35, 121]), + }, + assert: vec![assert::uplink_event(integration_pb::UplinkEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_name: t.name.clone(), + tenant_id: t.id.to_string(), + application_name: app.name.clone(), + application_id: app.id.to_string(), + device_profile_name: dp.name.clone(), + device_profile_id: dp.id.to_string(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + ..Default::default() + }), + dev_addr: "01020304".into(), + tx_info: Some(tx_info.clone()), + rx_info: vec![rx_info.clone()], + f_cnt: 10, + f_port: 1, + dr: 0, + data: vec![1, 2, 3, 4], + join_server_context: Some(integration_pb::JoinServerContext { + session_key_id: "010203".into(), + ..Default::default() + }), + ..Default::default() + })], + }, + Test { + name: "end-to-end encryption with AppSKey".into(), + device_queue_items: vec![], + before_func: None, + after_func: None, + device_session: Some(ds_app_s_key.clone()), + tx_info: tx_info.clone(), + rx_info: rx_info.clone(), + phy_payload: lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataUp, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]), + f_cnt: 10, + ..Default::default() + }, + f_port: Some(1), + frm_payload: Some(lrwn::FRMPayload::Raw(vec![1, 2, 3, 4])), + }), + mic: Some([104, 147, 35, 121]), + }, + assert: vec![assert::uplink_event(integration_pb::UplinkEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_name: t.name.clone(), + tenant_id: t.id.to_string(), + application_name: app.name.clone(), + application_id: app.id.to_string(), + device_profile_name: dp.name.clone(), + device_profile_id: dp.id.to_string(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + ..Default::default() + }), + dev_addr: "01020304".into(), + tx_info: Some(tx_info.clone()), + rx_info: vec![rx_info.clone()], + f_cnt: 10, + f_port: 1, + dr: 0, + data: vec![1, 2, 3, 4], + join_server_context: Some(integration_pb::JoinServerContext { + app_s_key: Some(common::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3], + }), + ..Default::default() + }), + ..Default::default() + })], + }, + Test { + name: "end-to-end encryption using AppSkey + encrypted downlink".into(), + device_queue_items: vec![device_queue::DeviceQueueItem { + id: Uuid::nil(), + dev_eui: dev.dev_eui.clone(), + f_port: 1, + data: vec![1, 2, 3, 4], + f_cnt_down: Some(10), + is_encrypted: true, + ..Default::default() + }], + before_func: None, + after_func: None, + device_session: Some(ds_app_s_key.clone()), + tx_info: tx_info.clone(), + rx_info: rx_info.clone(), + phy_payload: lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataUp, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]), + f_cnt: 10, + ..Default::default() + }, + f_port: Some(1), + frm_payload: Some(lrwn::FRMPayload::Raw(vec![1, 2, 3, 4])), + }), + mic: Some([104, 147, 35, 121]), + }, + assert: vec![ + assert::uplink_event(integration_pb::UplinkEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_name: t.name.clone(), + tenant_id: t.id.to_string(), + application_name: app.name.clone(), + application_id: app.id.to_string(), + device_profile_name: dp.name.clone(), + device_profile_id: dp.id.to_string(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + ..Default::default() + }), + dev_addr: "01020304".into(), + tx_info: Some(tx_info.clone()), + rx_info: vec![rx_info.clone()], + f_cnt: 10, + f_port: 1, + dr: 0, + data: vec![1, 2, 3, 4], + join_server_context: Some(integration_pb::JoinServerContext { + app_s_key: Some(common::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3], + }), + ..Default::default() + }), + ..Default::default() + }), + assert::f_cnt_up(dev.dev_eui.clone(), 11), + assert::n_f_cnt_down(dev.dev_eui.clone(), 5), + assert::downlink_phy_payloads(vec![ + lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataDown, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]), + f_cnt: 10, + f_ctrl: lrwn::FCtrl { + adr: true, + ..Default::default() + }, + ..Default::default() + }, + f_port: Some(1), + frm_payload: Some(lrwn::FRMPayload::Raw(vec![1, 2, 3, 4])), + }), + mic: Some([8, 125, 131, 36]), + }, + lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataDown, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]), + f_cnt: 10, + f_ctrl: lrwn::FCtrl { + adr: true, + ..Default::default() + }, + ..Default::default() + }, + f_port: Some(1), + frm_payload: Some(lrwn::FRMPayload::Raw(vec![1, 2, 3, 4])), + }), + mic: Some([8, 125, 131, 36]), + }, + ]), + ], + }, + ]; + + for tst in &tests { + run_test(tst).await; + } +} + #[tokio::test] async fn test_lorawan_11_uplink() { let _guard = test::prepare().await; diff --git a/chirpstack/src/test/mod.rs b/chirpstack/src/test/mod.rs index ee25c7dd..94512f01 100644 --- a/chirpstack/src/test/mod.rs +++ b/chirpstack/src/test/mod.rs @@ -9,6 +9,7 @@ mod class_a_test; mod class_b_test; mod class_c_test; mod multicast_test; +mod otaa_js_test; mod otaa_pr_test; mod otaa_test; mod relay_class_a_test; diff --git a/chirpstack/src/test/otaa_js_test.rs b/chirpstack/src/test/otaa_js_test.rs new file mode 100644 index 00000000..3dbf1d11 --- /dev/null +++ b/chirpstack/src/test/otaa_js_test.rs @@ -0,0 +1,396 @@ +use httpmock::prelude::*; +use uuid::Uuid; + +use super::assert; +use crate::storage::{application, device, device_profile, gateway, reset_redis, tenant}; +use crate::{ + backend::joinserver, config, gateway::backend as gateway_backend, integration, region, test, + uplink, +}; +use chirpstack_api::{common, gw, integration as integration_pb, internal}; +use lrwn::{DevAddr, EUI64}; + +struct Test { + name: String, + tx_info: gw::UplinkTxInfo, + rx_info: gw::UplinkRxInfo, + phy_payload: lrwn::PhyPayload, + js_response: backend::JoinAnsPayload, + assert: Vec, +} + +#[tokio::test] +async fn test_js() { + let _guard = test::prepare().await; + + let t = tenant::create(tenant::Tenant { + name: "tenant".into(), + can_have_gateways: true, + ..Default::default() + }) + .await + .unwrap(); + + let gw = gateway::create(gateway::Gateway { + name: "gw".into(), + tenant_id: t.id.clone(), + gateway_id: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + ..Default::default() + }) + .await + .unwrap(); + + let dp = device_profile::create(device_profile::DeviceProfile { + name: "dp".into(), + tenant_id: t.id.clone(), + region: lrwn::region::CommonName::EU868, + mac_version: lrwn::region::MacVersion::LORAWAN_1_0_3, + reg_params_revision: lrwn::region::Revision::A, + supports_otaa: true, + ..Default::default() + }) + .await + .unwrap(); + + let app = application::create(application::Application { + name: "app".into(), + tenant_id: t.id.clone(), + ..Default::default() + }) + .await + .unwrap(); + + let dev = device::create(device::Device { + name: "dev".into(), + application_id: app.id.clone(), + device_profile_id: dp.id.clone(), + dev_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + ..Default::default() + }) + .await + .unwrap(); + + let mut tx_info = gw::UplinkTxInfo { + frequency: 868100000, + ..Default::default() + }; + uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap(); + + let mut rx_info = gw::UplinkRxInfo { + gateway_id: gw.gateway_id.to_string(), + location: Some(Default::default()), + ..Default::default() + }; + rx_info + .metadata + .insert("region_config_id".to_string(), "eu868".to_string()); + rx_info + .metadata + .insert("region_common_name".to_string(), "EU868".to_string()); + + let phy = lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::JoinRequest, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::JoinRequest(lrwn::JoinRequestPayload { + join_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + dev_eui: dev.dev_eui.clone(), + dev_nonce: 1, + }), + mic: Some([1, 2, 3, 4]), + }; + + let phy_ja = lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::JoinAccept, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::JoinAccept(lrwn::JoinAcceptPayload { + join_nonce: 1, + home_netid: lrwn::NetID::from_be_bytes([0, 0, 0]), + devaddr: DevAddr::from_be_bytes([1, 2, 3, 4]), + dl_settings: lrwn::DLSettings { + opt_neg: false, + rx2_dr: 0, + rx1_dr_offset: 0, + }, + rx_delay: 1, + cflist: None, + }), + mic: Some([1, 2, 3, 4]), + }; + + let tests = vec![ + Test { + name: "test plain-text app_s_key".into(), + rx_info: rx_info.clone(), + tx_info: tx_info.clone(), + phy_payload: phy.clone(), + js_response: backend::JoinAnsPayload { + base: backend::BasePayloadResult { + base: backend::BasePayload { + sender_id: vec![1, 2, 3, 4, 5, 6, 7, 8], + receiver_id: vec![0, 0, 0], + message_type: backend::MessageType::JoinAns, + ..Default::default() + }, + result: backend::ResultPayload { + result_code: backend::ResultCode::Success, + ..Default::default() + }, + }, + phy_payload: phy_ja.to_vec().unwrap(), + app_s_key: Some(backend::KeyEnvelope { + kek_label: "".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + nwk_s_key: Some(backend::KeyEnvelope { + kek_label: "".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + ..Default::default() + }, + assert: vec![ + assert::device_session( + dev.dev_eui.clone(), + internal::DeviceSession { + dev_addr: vec![1, 2, 3, 4], + dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + mac_version: common::MacVersion::Lorawan103.into(), + f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + app_s_key: Some(common::KeyEnvelope { + kek_label: "".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + rx1_delay: 1, + rx2_frequency: 869525000, + enabled_uplink_channel_indices: vec![0, 1, 2], + nb_trans: 1, + region_config_id: "eu868".to_string(), + class_b_ping_slot_nb: 1, + ..Default::default() + }, + ), + assert::join_event(integration_pb::JoinEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + device_class_enabled: common::DeviceClass::ClassA.into(), + ..Default::default() + }), + dev_addr: "01020304".into(), + join_server_context: None, + ..Default::default() + }), + ], + }, + Test { + name: "test session_key_id".into(), + rx_info: rx_info.clone(), + tx_info: tx_info.clone(), + phy_payload: phy.clone(), + js_response: backend::JoinAnsPayload { + base: backend::BasePayloadResult { + base: backend::BasePayload { + sender_id: vec![1, 2, 3, 4, 5, 6, 7, 8], + receiver_id: vec![0, 0, 0], + message_type: backend::MessageType::JoinAns, + ..Default::default() + }, + result: backend::ResultPayload { + result_code: backend::ResultCode::Success, + ..Default::default() + }, + }, + phy_payload: phy_ja.to_vec().unwrap(), + session_key_id: vec![1, 2, 3, 4], + nwk_s_key: Some(backend::KeyEnvelope { + kek_label: "".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + ..Default::default() + }, + assert: vec![ + assert::device_session( + dev.dev_eui.clone(), + internal::DeviceSession { + dev_addr: vec![1, 2, 3, 4], + dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + mac_version: common::MacVersion::Lorawan103.into(), + f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + js_session_key_id: vec![1, 2, 3, 4], + rx1_delay: 1, + rx2_frequency: 869525000, + enabled_uplink_channel_indices: vec![0, 1, 2], + nb_trans: 1, + region_config_id: "eu868".to_string(), + class_b_ping_slot_nb: 1, + ..Default::default() + }, + ), + assert::join_event(integration_pb::JoinEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + device_class_enabled: common::DeviceClass::ClassA.into(), + ..Default::default() + }), + dev_addr: "01020304".into(), + join_server_context: Some(integration_pb::JoinServerContext { + session_key_id: "01020304".into(), + ..Default::default() + }), + ..Default::default() + }), + ], + }, + Test { + name: "test encrypted app_s_key".into(), + rx_info: rx_info.clone(), + tx_info: tx_info.clone(), + phy_payload: phy.clone(), + js_response: backend::JoinAnsPayload { + base: backend::BasePayloadResult { + base: backend::BasePayload { + sender_id: vec![1, 2, 3, 4, 5, 6, 7, 8], + receiver_id: vec![0, 0, 0], + message_type: backend::MessageType::JoinAns, + ..Default::default() + }, + result: backend::ResultPayload { + result_code: backend::ResultCode::Success, + ..Default::default() + }, + }, + phy_payload: phy_ja.to_vec().unwrap(), + app_s_key: Some(backend::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + nwk_s_key: Some(backend::KeyEnvelope { + kek_label: "".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + ..Default::default() + }, + assert: vec![ + assert::device_session( + dev.dev_eui.clone(), + internal::DeviceSession { + dev_addr: vec![1, 2, 3, 4], + dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + join_eui: vec![1, 2, 3, 4, 5, 6, 7, 8], + mac_version: common::MacVersion::Lorawan103.into(), + f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + nwk_s_enc_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + app_s_key: Some(common::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + rx1_delay: 1, + rx2_frequency: 869525000, + enabled_uplink_channel_indices: vec![0, 1, 2], + nb_trans: 1, + region_config_id: "eu868".to_string(), + class_b_ping_slot_nb: 1, + ..Default::default() + }, + ), + assert::join_event(integration_pb::JoinEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp.id.to_string(), + device_profile_name: dp.name.clone(), + device_name: dev.name.clone(), + dev_eui: dev.dev_eui.to_string(), + device_class_enabled: common::DeviceClass::ClassA.into(), + ..Default::default() + }), + dev_addr: "01020304".into(), + join_server_context: Some(integration_pb::JoinServerContext { + app_s_key: Some(common::KeyEnvelope { + kek_label: "kek-label".into(), + aes_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8], + }), + ..Default::default() + }), + ..Default::default() + }), + ], + }, + ]; + + for tst in &tests { + run_test(tst).await; + } +} + +async fn run_test(t: &Test) { + println!("> {}", t.name); + + reset_redis().await.unwrap(); + + let server = MockServer::start(); + let mut js_mock = server.mock(|when, then| { + when.method(POST).path("/"); + + then.body(serde_json::to_string(&t.js_response).unwrap()); + }); + + let mut conf: config::Configuration = (*config::get()).clone(); + conf.join_server.servers.push(config::JoinServerServer { + join_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + server: server.url("/"), + ..Default::default() + }); + config::set(conf); + region::setup().unwrap(); + joinserver::setup().unwrap(); + + integration::set_mock().await; + gateway_backend::set_backend(&"eu868", Box::new(gateway_backend::mock::Backend {})).await; + + integration::mock::reset().await; + gateway_backend::mock::reset().await; + + uplink::handle_uplink( + Uuid::new_v4(), + gw::UplinkFrameSet { + phy_payload: t.phy_payload.to_vec().unwrap(), + tx_info: Some(t.tx_info.clone()), + rx_info: vec![t.rx_info.clone()], + }, + ) + .await + .unwrap(); + + js_mock.assert(); + js_mock.delete(); + + for assert in &t.assert { + assert().await; + } +} diff --git a/chirpstack/src/test/otaa_pr_test.rs b/chirpstack/src/test/otaa_pr_test.rs index ce7c7924..eb00fb11 100644 --- a/chirpstack/src/test/otaa_pr_test.rs +++ b/chirpstack/src/test/otaa_pr_test.rs @@ -190,6 +190,7 @@ async fn test_fns() { }); gateway_backend::set_backend(&"eu868", Box::new(gateway_backend::mock::Backend {})).await; + gateway_backend::mock::reset().await; // Simulate uplink uplink::handle_uplink( diff --git a/chirpstack/src/uplink/data.rs b/chirpstack/src/uplink/data.rs index c87c5cca..48dd0d26 100644 --- a/chirpstack/src/uplink/data.rs +++ b/chirpstack/src/uplink/data.rs @@ -637,10 +637,11 @@ impl Data { if f_port == 0 || f_port == lrwn::LA_FPORT_RELAY { let nwk_s_enc_key = AES128Key::from_slice(&ds.nwk_s_enc_key)?; self.phy_payload.decrypt_frm_payload(&nwk_s_enc_key)?; - } else if ds.app_s_key.is_some() { - let app_s_key = AES128Key::from_slice(&ds.app_s_key.as_ref().unwrap().aes_key)?; - - self.phy_payload.decrypt_frm_payload(&app_s_key)?; + } else if !self._is_end_to_end_encrypted() { + if let Some(app_s_key) = &ds.app_s_key { + let app_s_key = AES128Key::from_slice(&app_s_key.aes_key)?; + self.phy_payload.decrypt_frm_payload(&app_s_key)?; + } } Ok(()) @@ -935,6 +936,7 @@ impl Data { let app = self.application.as_ref().unwrap(); let dp = self.device_profile.as_ref().unwrap(); let dev = self.device.as_ref().unwrap(); + let ds = self.device_session.as_ref().unwrap(); let mac = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload { pl } else { @@ -959,39 +961,52 @@ impl Data { object: None, rx_info: self.uplink_frame_set.rx_info_set.clone(), tx_info: Some(self.uplink_frame_set.tx_info.clone()), + join_server_context: if self._is_end_to_end_encrypted() { + Some(integration_pb::JoinServerContext { + session_key_id: hex::encode(&ds.js_session_key_id), + app_s_key: ds.app_s_key.clone(), + }) + } else { + None + }, }; - pl.object = match codec::binary_to_struct( - dp.payload_codec_runtime, - ts, - mac.f_port.unwrap_or(0), - &dev.variables, - &dp.payload_codec_script, - &pl.data, - ) - .await - { - Ok(v) => v, - Err(e) => { - integration::log_event( - app.id, - &dev.variables, - &integration_pb::LogEvent { - time: Some(Utc::now().into()), - device_info: self.device_info.clone(), - level: integration_pb::LogLevel::Error.into(), - code: integration_pb::LogCode::UplinkCodec.into(), - description: format!("{}", e), - context: [("deduplication_id".to_string(), pl.deduplication_id.clone())] + if !self._is_end_to_end_encrypted() { + pl.object = match codec::binary_to_struct( + dp.payload_codec_runtime, + ts, + mac.f_port.unwrap_or(0), + &dev.variables, + &dp.payload_codec_script, + &pl.data, + ) + .await + { + Ok(v) => v, + Err(e) => { + integration::log_event( + app.id, + &dev.variables, + &integration_pb::LogEvent { + time: Some(Utc::now().into()), + device_info: self.device_info.clone(), + level: integration_pb::LogLevel::Error.into(), + code: integration_pb::LogCode::UplinkCodec.into(), + description: format!("{}", e), + context: [( + "deduplication_id".to_string(), + pl.deduplication_id.clone(), + )] .iter() .cloned() .collect(), - }, - ) - .await; - None - } - }; + }, + ) + .await; + None + } + }; + } integration::uplink_event(app.id, &dev.variables, &pl).await; @@ -1356,4 +1371,20 @@ impl Data { false } + + fn _is_end_to_end_encrypted(&self) -> bool { + let ds = self.device_session.as_ref().unwrap(); + + if !ds.js_session_key_id.is_empty() { + return true; + } + + if let Some(app_s_key) = &ds.app_s_key { + if !app_s_key.kek_label.is_empty() { + return true; + } + } + + false + } } diff --git a/chirpstack/src/uplink/join.rs b/chirpstack/src/uplink/join.rs index 895f1f35..17a81308 100644 --- a/chirpstack/src/uplink/join.rs +++ b/chirpstack/src/uplink/join.rs @@ -52,6 +52,7 @@ pub struct JoinRequest { s_nwk_s_int_key: Option, nwk_s_enc_key: Option, app_s_key: Option, + js_session_key_id: Vec, } impl JoinRequest { @@ -108,11 +109,12 @@ impl JoinRequest { s_nwk_s_int_key: None, nwk_s_enc_key: None, app_s_key: None, + js_session_key_id: vec![], }; ctx.get_join_request_payload()?; ctx.get_device_or_try_pr_roaming().await?; - ctx.get_js_client()?; + ctx.get_device_keys_or_js_client().await?; // used to validate MIC + if we need external JS ctx.get_application().await?; ctx.get_tenant().await?; ctx.get_device_profile().await?; @@ -165,11 +167,12 @@ impl JoinRequest { s_nwk_s_int_key: None, nwk_s_enc_key: None, app_s_key: None, + js_session_key_id: vec![], }; ctx.get_join_request_payload_relayed()?; ctx.get_device().await?; - ctx.get_js_client()?; + ctx.get_device_keys_or_js_client().await?; ctx.get_application().await?; ctx.get_tenant().await?; ctx.get_device_profile().await?; @@ -227,20 +230,6 @@ impl JoinRequest { Ok(()) } - fn get_js_client(&mut self) -> Result<()> { - let jr = self.join_request.as_ref().unwrap(); - - trace!(join_eui = %jr.join_eui, "Trying to get Join Server client"); - if let Ok(v) = joinserver::get(&jr.join_eui) { - trace!("Found Join Server client"); - self.js_client = Some(v); - } else { - trace!("Join Server client does not exist"); - } - - Ok(()) - } - async fn get_device(&mut self) -> Result<()> { trace!("Getting device"); let jr = self.join_request.as_ref().unwrap(); @@ -249,6 +238,29 @@ impl JoinRequest { Ok(()) } + // We need to get either the device-keys or a JS client. In any other case, this must return an error. + async fn get_device_keys_or_js_client(&mut self) -> Result<()> { + trace!("Getting device keys"); + let jr = self.join_request.as_ref().unwrap(); + self.device_keys = match device_keys::get(&jr.dev_eui).await { + Ok(v) => Some(v), + Err(e) => { + if let StorageError::NotFound(_) = e { + None + } else { + return Err(anyhow::Error::new(e)); + } + } + }; + + if !self.device_keys.is_some() { + trace!(join_eui = %jr.join_eui, "Getting Join Server client"); + self.js_client = Some(joinserver::get(&jr.join_eui)?); + } + + Ok(()) + } + async fn get_device_or_try_pr_roaming(&mut self) -> Result<()> { trace!("Getting device"); let jr = self.join_request.as_ref().unwrap(); @@ -391,8 +403,7 @@ impl JoinRequest { } async fn validate_mic(&self) -> Result<()> { - let join_request = self.join_request.as_ref().unwrap(); - let device_keys = device_keys::get(&join_request.dev_eui).await?; + let device_keys = self.device_keys.as_ref().unwrap(); if let Some(relay_ctx) = self.relay_context.as_ref() { if relay_ctx @@ -571,6 +582,7 @@ impl JoinRequest { aes_key: v.aes_key.clone(), }); } + self.js_session_key_id = join_ans_pl.session_key_id.clone(); if let Some(v) = &join_ans_pl.nwk_s_key { let key = keywrap::unwrap(v).context("Unwrap nwk_s_key")?; @@ -776,6 +788,7 @@ impl JoinRequest { s_nwk_s_int_key: self.s_nwk_s_int_key.as_ref().unwrap().to_vec(), nwk_s_enc_key: self.nwk_s_enc_key.as_ref().unwrap().to_vec(), app_s_key: self.app_s_key.clone(), + js_session_key_id: self.js_session_key_id.clone(), rx1_delay: region_network.rx1_delay.into(), rx1_dr_offset: region_network.rx1_dr_offset.into(), rx2_dr: region_network.rx2_dr.into(), @@ -928,6 +941,26 @@ impl JoinRequest { device_info: self.device_info.clone(), relay_rx_info: self.relay_rx_info.clone(), dev_addr: self.dev_addr.as_ref().unwrap().to_string(), + join_server_context: if !self.js_session_key_id.is_empty() { + Some(integration_pb::JoinServerContext { + app_s_key: None, + session_key_id: hex::encode(&self.js_session_key_id), + }) + } else if let Some(app_s_key) = &self.app_s_key { + if app_s_key.kek_label.is_empty() { + None + } else { + Some(integration_pb::JoinServerContext { + app_s_key: Some(common::KeyEnvelope { + kek_label: app_s_key.kek_label.clone(), + aes_key: app_s_key.aes_key.clone(), + }), + session_key_id: "".into(), + }) + } + } else { + None + }, }; integration::join_event(app.id, &dev.variables, &pl).await; diff --git a/chirpstack/src/uplink/join_sns.rs b/chirpstack/src/uplink/join_sns.rs index 9df2dd0a..b0dcade2 100644 --- a/chirpstack/src/uplink/join_sns.rs +++ b/chirpstack/src/uplink/join_sns.rs @@ -39,6 +39,7 @@ pub struct JoinRequest { s_nwk_s_int_key: Option, nwk_s_enc_key: Option, app_s_key: Option, + js_session_key_id: String, } impl JoinRequest { @@ -76,11 +77,12 @@ impl JoinRequest { s_nwk_s_int_key: None, nwk_s_enc_key: None, app_s_key: None, + js_session_key_id: "".to_string(), }; ctx.get_join_request_payload()?; ctx.get_device().await?; - ctx.get_js_client()?; + ctx.get_device_keys_or_js_client().await?; ctx.get_application().await?; ctx.get_tenant().await?; ctx.get_device_profile().await?; @@ -128,15 +130,24 @@ impl JoinRequest { Ok(()) } - fn get_js_client(&mut self) -> Result<()> { + // We need to get either the device-keys or a JS client. In any other case, this must return an error. + async fn get_device_keys_or_js_client(&mut self) -> Result<()> { + trace!("Getting device keys"); let jr = self.join_request.as_ref().unwrap(); + self.device_keys = match device_keys::get(&jr.dev_eui).await { + Ok(v) => Some(v), + Err(e) => { + if let StorageError::NotFound(_) = e { + None + } else { + return Err(anyhow::Error::new(e)); + } + } + }; - trace!(join_eui = %jr.join_eui, "Trying to get Join Server client"); - if let Ok(v) = joinserver::get(&jr.join_eui) { - trace!("Found Join Server client"); - self.js_client = Some(v); - } else { - trace!("Join Server client does not exist"); + if !self.device_keys.is_some() { + trace!(join_eui = %jr.join_eui, "Getting Join Server client"); + self.js_client = Some(joinserver::get(&jr.join_eui)?); } Ok(()) @@ -258,6 +269,7 @@ impl JoinRequest { aes_key: v.aes_key.clone(), }); } + self.js_session_key_id = hex::encode(join_ans_pl.session_key_id); if let Some(v) = &join_ans_pl.nwk_s_key { let key = keywrap::unwrap(v).context("Unwrap nwk_s_key")?; @@ -289,8 +301,7 @@ impl JoinRequest { } async fn validate_mic(&self) -> Result<()> { - let join_request = self.join_request.as_ref().unwrap(); - let device_keys = device_keys::get(&join_request.dev_eui).await?; + let device_keys = self.device_keys.as_ref().unwrap(); if self .uplink_frame_set @@ -678,6 +689,26 @@ impl JoinRequest { device_info: self.device_info.clone(), relay_rx_info: None, dev_addr: self.dev_addr.as_ref().unwrap().to_string(), + join_server_context: if !self.js_session_key_id.is_empty() { + Some(integration_pb::JoinServerContext { + app_s_key: None, + session_key_id: self.js_session_key_id.clone(), + }) + } else if let Some(app_s_key) = &self.app_s_key { + if app_s_key.kek_label.is_empty() { + None + } else { + Some(integration_pb::JoinServerContext { + app_s_key: Some(common::KeyEnvelope { + kek_label: app_s_key.kek_label.clone(), + aes_key: app_s_key.aes_key.clone(), + }), + session_key_id: "".to_string(), + }) + } + } else { + None + }, }; integration::join_event(app.id, &dev.variables, &pl).await; diff --git a/ui/src/views/devices/DeviceQueue.tsx b/ui/src/views/devices/DeviceQueue.tsx index 2eec7162..5ba8ca1c 100644 --- a/ui/src/views/devices/DeviceQueue.tsx +++ b/ui/src/views/devices/DeviceQueue.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { Struct } from "google-protobuf/google/protobuf/struct_pb"; -import { notification } from "antd"; +import { Switch, notification } from "antd"; import { Button, Tabs, Space, Card, Row, Form, Input, InputNumber, Checkbox, Popconfirm } from "antd"; import { ColumnsType } from "antd/es/table"; import { RedoOutlined, DeleteOutlined } from "@ant-design/icons"; @@ -28,6 +28,7 @@ interface IProps { function DeviceQueue(props: IProps) { const [refreshCounter, setRefreshCounter] = useState(0); + const [isEncrypted, setIsEncrypted] = useState(false); const [form] = Form.useForm(); const columns: ColumnsType = [ @@ -38,7 +39,7 @@ function DeviceQueue(props: IProps) { width: 350, }, { - title: "Is pending", + title: "Pending", dataIndex: "isPending", key: "isPending", width: 100, @@ -50,13 +51,26 @@ function DeviceQueue(props: IProps) { } }, }, + { + title: "Encrypted", + dataIndex: "isEncrypted", + key: "isEncrypted", + width: 100, + render: (text, record) => { + if (record.isEncrypted) { + return "yes"; + } else { + return "no"; + } + }, + }, { title: "Frame-counter", dataIndex: "fCntDown", key: "fCntDown", width: 200, render: (text, record) => { - if (record.isPending === true) { + if (record.isPending === true || record.isEncrypted === true) { return record.fCntDown; } else { return ""; @@ -121,6 +135,8 @@ function DeviceQueue(props: IProps) { item.setDevEui(props.device.getDevEui()); item.setFPort(values.fPort); item.setConfirmed(values.confirmed); + item.setIsEncrypted(values.isEncrypted); + item.setFCntDown(values.fCntDown); if (values.hex !== undefined) { item.setData(new Uint8Array(Buffer.from(values.hex, "hex"))); @@ -151,6 +167,7 @@ function DeviceQueue(props: IProps) { DeviceStore.enqueue(req, _ => { form.resetFields(); + setIsEncrypted(false); refreshQueue(); }); }; @@ -162,11 +179,26 @@ function DeviceQueue(props: IProps) { - + + + + + {isEncrypted && ( + + )}