dji_mqtt_connect 0.1.1.4 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop/rspec.yml +2 -2
- data/Gemfile.lock +20 -9
- data/dji_mqtt_connect.gemspec +48 -0
- data/lib/dji_mqtt_connect/factories.rb +65 -0
- data/lib/dji_mqtt_connect/marshals/thing/product/osd_marshal.rb +17 -3
- data/lib/dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_message.rb +15 -0
- data/lib/dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_reply_message.rb +34 -0
- data/lib/dji_mqtt_connect/messages/thing/product/dock_osd_message.rb +199 -0
- data/lib/dji_mqtt_connect/messages/thing/product/drone_osd_message.rb +114 -10
- data/lib/dji_mqtt_connect/messages/thing/product/remote_osd_message.rb +4 -9
- data/lib/dji_mqtt_connect/messages/thing/product/storage_config_get_requests_message.rb +14 -0
- data/lib/dji_mqtt_connect/messages/thing/product/storage_config_get_requests_reply_message.rb +44 -0
- data/lib/dji_mqtt_connect/mixins/latitude_conditional.rb +12 -0
- data/lib/dji_mqtt_connect/mixins/longitude_conditional.rb +12 -0
- data/lib/dji_mqtt_connect/mixins/temperature_conditional.rb +12 -0
- data/lib/dji_mqtt_connect/topics/sys/product/status.rb +8 -1
- data/lib/dji_mqtt_connect/topics/thing/product/osd.rb +8 -3
- data/lib/dji_mqtt_connect/topics/thing/product/requests.rb +8 -1
- data/lib/dji_mqtt_connect/types.rb +3 -0
- data/lib/dji_mqtt_connect/utils/message_sanitizer.rb +90 -0
- data/lib/dji_mqtt_connect/utils/message_schema_generator.rb +39 -0
- data/lib/dji_mqtt_connect/utils/mqttx_fixture_data_generator.rb +68 -0
- data/lib/dji_mqtt_connect/version.rb +1 -1
- data/lib/dji_mqtt_connect.rb +15 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 824a4b8942fc65705e25588a32df620e49602573b1eb14ed6f1b2c34c98023cd
|
4
|
+
data.tar.gz: 7f9dc5c06e40c04862baed3c41b8342da77a9fbedb083ab7432b9173180ebce1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58ccec055d15866882fd93521c5d312719012c10e8778e18b99191c2bb48a7173ac0283f7f18daf21fb950d0472c02b3cb861cd1d07b0da0584e62278d62554c
|
7
|
+
data.tar.gz: ac4f30aa186b37fa04dbadbc49d813648c08ca031809a7c3b73d97fb0d6f7489cc680d74e0a3a83015ebcf77d26d25386c7f8dbecdb4e04e337308bc3a1f7a6c
|
data/.rubocop/rspec.yml
CHANGED
@@ -48,8 +48,8 @@ RSpec/ReceiveCounts:
|
|
48
48
|
Capybara/CurrentPathExpectation:
|
49
49
|
Enabled: true
|
50
50
|
|
51
|
-
|
51
|
+
FactoryBot/AttributeDefinedStatically:
|
52
52
|
Enabled: true
|
53
53
|
|
54
|
-
|
54
|
+
FactoryBot/CreateList:
|
55
55
|
Enabled: true
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dji_mqtt_connect (0.1.
|
4
|
+
dji_mqtt_connect (0.1.2)
|
5
5
|
activesupport (>= 6.0, <= 8)
|
6
6
|
dry-struct (~> 1.6)
|
7
7
|
dry-transformer (~> 1.0)
|
@@ -46,15 +46,16 @@ GEM
|
|
46
46
|
zeitwerk (~> 2.6)
|
47
47
|
globalid (1.1.0)
|
48
48
|
activesupport (>= 5.0)
|
49
|
-
i18n (1.
|
49
|
+
i18n (1.13.0)
|
50
50
|
concurrent-ruby (~> 1.0)
|
51
51
|
ice_nine (0.11.2)
|
52
52
|
json (2.6.3)
|
53
53
|
language_server-protocol (3.17.0.3)
|
54
|
+
lint_roller (1.0.0)
|
54
55
|
minitest (5.18.0)
|
55
56
|
mqtt (0.6.0)
|
56
57
|
parallel (1.23.0)
|
57
|
-
parser (3.2.2.
|
58
|
+
parser (3.2.2.1)
|
58
59
|
ast (~> 2.4.1)
|
59
60
|
rainbow (3.1.1)
|
60
61
|
rake (13.0.6)
|
@@ -66,7 +67,7 @@ GEM
|
|
66
67
|
rspec-mocks (~> 3.12.0)
|
67
68
|
rspec-core (3.12.2)
|
68
69
|
rspec-support (~> 3.12.0)
|
69
|
-
rspec-expectations (3.12.
|
70
|
+
rspec-expectations (3.12.3)
|
70
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
71
72
|
rspec-support (~> 3.12.0)
|
72
73
|
rspec-mocks (3.12.5)
|
@@ -83,18 +84,21 @@ GEM
|
|
83
84
|
rubocop-ast (>= 1.28.0, < 2.0)
|
84
85
|
ruby-progressbar (~> 1.7)
|
85
86
|
unicode-display_width (>= 2.4.0, < 3.0)
|
86
|
-
rubocop-ast (1.28.
|
87
|
+
rubocop-ast (1.28.1)
|
87
88
|
parser (>= 3.2.1.0)
|
88
|
-
rubocop-capybara (2.
|
89
|
+
rubocop-capybara (2.18.0)
|
89
90
|
rubocop (~> 1.41)
|
91
|
+
rubocop-factory_bot (2.22.0)
|
92
|
+
rubocop (~> 1.33)
|
90
93
|
rubocop-performance (1.16.0)
|
91
94
|
rubocop (>= 1.7.0, < 2.0)
|
92
95
|
rubocop-ast (>= 0.4.0)
|
93
96
|
rubocop-rake (0.6.0)
|
94
97
|
rubocop (~> 1.0)
|
95
|
-
rubocop-rspec (2.
|
98
|
+
rubocop-rspec (2.22.0)
|
96
99
|
rubocop (~> 1.33)
|
97
100
|
rubocop-capybara (~> 2.17)
|
101
|
+
rubocop-factory_bot (~> 2.22)
|
98
102
|
ruby-progressbar (1.13.0)
|
99
103
|
simplecov (0.22.0)
|
100
104
|
docile (~> 1.1)
|
@@ -102,16 +106,23 @@ GEM
|
|
102
106
|
simplecov_json_formatter (~> 0.1)
|
103
107
|
simplecov-html (0.12.3)
|
104
108
|
simplecov_json_formatter (0.1.4)
|
105
|
-
standard (1.
|
109
|
+
standard (1.28.2)
|
106
110
|
language_server-protocol (~> 3.17.0.2)
|
111
|
+
lint_roller (~> 1.0)
|
107
112
|
rubocop (~> 1.50.2)
|
113
|
+
standard-custom (~> 1.0.0)
|
114
|
+
standard-performance (~> 1.0.1)
|
115
|
+
standard-custom (1.0.0)
|
116
|
+
lint_roller (~> 1.0)
|
117
|
+
standard-performance (1.0.1)
|
118
|
+
lint_roller (~> 1.0)
|
108
119
|
rubocop-performance (~> 1.16.0)
|
109
120
|
tzinfo (2.0.6)
|
110
121
|
concurrent-ruby (~> 1.0)
|
111
122
|
unicode-display_width (2.4.2)
|
112
123
|
wisper (2.0.1)
|
113
124
|
wisper-rspec (1.1.0)
|
114
|
-
zeitwerk (2.6.
|
125
|
+
zeitwerk (2.6.8)
|
115
126
|
|
116
127
|
PLATFORMS
|
117
128
|
x86_64-darwin-21
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/dji_mqtt_connect/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "dji_mqtt_connect"
|
7
|
+
spec.version = DjiMqttConnect::VERSION
|
8
|
+
spec.authors = ["Sphere Drones"]
|
9
|
+
spec.email = ["projects@spheregroup.com.au"]
|
10
|
+
|
11
|
+
spec.summary = "Manages MQTT Connections with DJI Drones"
|
12
|
+
# spec.description = "TODO: Write a longer description or delete this line."
|
13
|
+
spec.homepage = "https://github.com/sphere-drones/dji_mqtt_connect"
|
14
|
+
spec.required_ruby_version = ">= 3.1.0"
|
15
|
+
|
16
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/sphere-drones/dji_mqtt_connect"
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/sphere-drones/dji_mqtt_connect/main/CHANGELOG.md"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(__dir__) do
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
# Use Active Support for inflection and logging
|
34
|
+
spec.add_dependency "activesupport", ">= 6.0", "<= 8"
|
35
|
+
|
36
|
+
# Use Dry-rb for valid POROs
|
37
|
+
spec.add_dependency "dry-struct", "~> 1.6"
|
38
|
+
spec.add_dependency "dry-transformer", "~> 1.0"
|
39
|
+
|
40
|
+
# Use MQTT as a client for receiving/sending messages
|
41
|
+
spec.add_dependency "mqtt", "~> 0.6.0"
|
42
|
+
|
43
|
+
# Use Whisper for events
|
44
|
+
spec.add_dependency "wisper", "~> 2.0"
|
45
|
+
|
46
|
+
# For more information and examples about making a new gem, check out our
|
47
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
48
|
+
end
|
@@ -114,5 +114,70 @@ module DjiMqttConnect
|
|
114
114
|
end
|
115
115
|
)
|
116
116
|
end
|
117
|
+
|
118
|
+
def build_thing_product_airport_organization_get_requests_message
|
119
|
+
message_data = {
|
120
|
+
device_binding_code: "device_binding_code",
|
121
|
+
organization_id: "organization_id"
|
122
|
+
}
|
123
|
+
|
124
|
+
Thing::Product::AirportOrganizationGetRequestsMessage.new(
|
125
|
+
_method: "airport_organization_get",
|
126
|
+
_data: message_data,
|
127
|
+
tid: "3093aad5-bb98-5026-685a-cea540eb9267",
|
128
|
+
bid: "479533e0-cb01-933c-3758-22f2a82abeb8",
|
129
|
+
timestamp: 1679380233000,
|
130
|
+
data: message_data
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_thing_product_airport_organization_get_requests_reply_message
|
135
|
+
airport_organization_get_request = build_thing_product_airport_organization_get_requests_message
|
136
|
+
|
137
|
+
Thing::Product::AirportOrganizationGetRequestsReplyMessage.build_for(
|
138
|
+
airport_organization_get_request,
|
139
|
+
result: 0,
|
140
|
+
output: {
|
141
|
+
organization_name: "12345"
|
142
|
+
}
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
def build_thing_product_storage_config_get_requests_message
|
147
|
+
message_data = {
|
148
|
+
module: 0
|
149
|
+
}
|
150
|
+
|
151
|
+
Thing::Product::StorageConfigGetRequestsMessage.new(
|
152
|
+
_method: "storage_config_get",
|
153
|
+
_data: message_data,
|
154
|
+
tid: "3093aad5-bb98-5026-685a-cea540eb9267",
|
155
|
+
bid: "479533e0-cb01-933c-3758-22f2a82abeb8",
|
156
|
+
timestamp: 1679380233000,
|
157
|
+
data: message_data
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
def build_thing_product_storage_config_get_requests_reply_message
|
162
|
+
storage_config_get_request = build_thing_product_storage_config_get_requests_message
|
163
|
+
|
164
|
+
Thing::Product::StorageConfigGetRequestsReplyMessage.build_for(
|
165
|
+
storage_config_get_request,
|
166
|
+
result: 0,
|
167
|
+
output: {
|
168
|
+
bucket: "bucket_name",
|
169
|
+
credentials: {
|
170
|
+
access_key_id: "access_key_id",
|
171
|
+
access_key_secret: "access_key_secret",
|
172
|
+
expire: 3600,
|
173
|
+
security_token: "security_token"
|
174
|
+
},
|
175
|
+
endpoint: "https://oss-cn-hangzhou.aliyuncs.com",
|
176
|
+
object_key_prefix: "b4cfaae6-bd9d-4cd0-8472-63b608c3c581",
|
177
|
+
provider: "ali",
|
178
|
+
region: "hz"
|
179
|
+
}
|
180
|
+
)
|
181
|
+
end
|
117
182
|
end
|
118
183
|
end
|
@@ -12,10 +12,15 @@ module DjiMqttConnect
|
|
12
12
|
import Dry::Transformer::HashTransformations
|
13
13
|
|
14
14
|
define! do
|
15
|
+
# Ensure there is a data element
|
16
|
+
map_value "data", ->(data) { data || {} }
|
17
|
+
|
18
|
+
# Create a backup for unsupported attributes
|
15
19
|
copy_keys "data" => "_data"
|
16
20
|
|
17
21
|
map_value "data" do
|
18
|
-
|
22
|
+
# Rename the invalid (for Ruby) wireless link attributes
|
23
|
+
guard ->(data) { data.key?("wireless_link") } do
|
19
24
|
# Translate wireless_link 4g keys (from remote payload)
|
20
25
|
map_value "wireless_link" do
|
21
26
|
rename_keys "4g_freq_band" => "_4g_freq_band",
|
@@ -38,6 +43,8 @@ module DjiMqttConnect
|
|
38
43
|
DroneOsdMessage
|
39
44
|
elsif remote_osd_data_attributes.all? { |attribute| message_data.key?(attribute.to_s) }
|
40
45
|
RemoteOsdMessage
|
46
|
+
elsif dock_osd_data_attributes.any? { |attribute| message_data.key?(attribute.to_s) }
|
47
|
+
DockOsdMessage
|
41
48
|
else
|
42
49
|
OsdMessage
|
43
50
|
end
|
@@ -57,12 +64,19 @@ module DjiMqttConnect
|
|
57
64
|
@attribute_transformer ||= AttributeTransformer.new
|
58
65
|
end
|
59
66
|
|
67
|
+
def dock_osd_data_attributes
|
68
|
+
@dock_osd_data_attributes ||= DockOsdMessage::STABLE_DATA_ATTRIBUTES.map(&:to_s)
|
69
|
+
end
|
70
|
+
|
60
71
|
def drone_osd_data_attributes
|
61
|
-
@drone_osd_data_attributes ||=
|
72
|
+
@drone_osd_data_attributes ||= DroneOsdMessage::STABLE_DATA_ATTRIBUTES.map(&:to_s)
|
62
73
|
end
|
63
74
|
|
64
75
|
def remote_osd_data_attributes
|
65
|
-
@remote_osd_data_attributes ||=
|
76
|
+
@remote_osd_data_attributes ||= begin
|
77
|
+
data_keys = DjiMqttConnect::Thing::Product::RemoteOsdMessage::Data.schema.keys
|
78
|
+
data_keys.select(&:required?).map(&:name)
|
79
|
+
end
|
66
80
|
end
|
67
81
|
end
|
68
82
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Thing::Product
|
5
|
+
# https://developer.dji.com/doc/cloud-api-tutorial/en/server-api-reference/mqtt/thing-model/gateway/dock/organization.html#search-for-the-organization-information-that-device-bound-to
|
6
|
+
class AirportOrganizationGetRequestsMessage < RequestsMessage
|
7
|
+
attribute :_method, Types::String.enum("airport_organization_get")
|
8
|
+
|
9
|
+
attribute :data do
|
10
|
+
attribute :device_binding_code, Types::String
|
11
|
+
attribute :organization_id, Types::String
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_reply_message.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Thing::Product
|
5
|
+
# https://developer.dji.com/doc/cloud-api-tutorial/en/server-api-reference/mqtt/thing-model/gateway/dock/organization.html#search-for-the-organization-information-that-device-bound-to
|
6
|
+
class AirportOrganizationGetRequestsReplyMessage < Message
|
7
|
+
def self.build_for(airport_organization_get_message, output:, result: 0)
|
8
|
+
new(
|
9
|
+
_method: airport_organization_get_message._method,
|
10
|
+
tid: airport_organization_get_message.tid,
|
11
|
+
bid: airport_organization_get_message.bid,
|
12
|
+
timestamp: Time.now.strftime("%s%L").to_i,
|
13
|
+
data: {
|
14
|
+
result: result,
|
15
|
+
output: output
|
16
|
+
}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
attribute :tid, Types::UUID
|
21
|
+
attribute :bid, Types::UUID
|
22
|
+
attribute :timestamp, Types::Timestamp
|
23
|
+
|
24
|
+
attribute :_method, Types::String.enum("airport_organization_get")
|
25
|
+
|
26
|
+
attribute :data do
|
27
|
+
attribute :result, Types::Integer
|
28
|
+
attribute :output do
|
29
|
+
attribute :organization_name, Types::String
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Thing::Product
|
5
|
+
class DockOsdMessage < OsdMessage
|
6
|
+
schema schema.strict
|
7
|
+
|
8
|
+
STABLE_DATA_ATTRIBUTES = %i[
|
9
|
+
drc_state
|
10
|
+
drone_in_dock
|
11
|
+
job_number
|
12
|
+
]
|
13
|
+
|
14
|
+
attribute :data do
|
15
|
+
include Mixins::LatitudeConditional
|
16
|
+
include Mixins::LongitudeConditional
|
17
|
+
include Mixins::TemperatureConditional
|
18
|
+
|
19
|
+
attribute? :acc_time, Types::Integer
|
20
|
+
attribute? :activation_time, Types::Integer
|
21
|
+
|
22
|
+
attribute? :backup_battery do
|
23
|
+
include Mixins::TemperatureConditional
|
24
|
+
|
25
|
+
attribute :switch, Types::Integer.enum(0, 1)
|
26
|
+
attribute :voltage, Types::Integer
|
27
|
+
attribute :temperature, Types::Temperature
|
28
|
+
end
|
29
|
+
|
30
|
+
# {"0":"Disconnected","1":"Connecting","2":"Connected"}
|
31
|
+
attribute? :drc_state, Types::Integer.enum(0, 1, 2)
|
32
|
+
|
33
|
+
attribute? :drone_battery_maintenance_info do
|
34
|
+
attribute :batteries, Types::Array do
|
35
|
+
include Mixins::TemperatureConditional
|
36
|
+
|
37
|
+
attribute :index, Types::Integer
|
38
|
+
attribute :capacity_percent, Types::Integer
|
39
|
+
attribute :voltage, Types::Integer
|
40
|
+
attribute :temperature, Types::Temperature
|
41
|
+
end
|
42
|
+
|
43
|
+
# {"0":"Battery is not in heating or insulation","1":"Battery is in heating","2":"Battery is in insulation"}
|
44
|
+
attribute? :heat_state, Types::Integer.enum(0, 1, 2)
|
45
|
+
|
46
|
+
# {"0":"No need to maintenance","1":"Need maintenance","2":"Under maintenance"}
|
47
|
+
attribute? :maintenance_state, Types::Integer
|
48
|
+
attribute? :maintenance_time_left, Types::Integer
|
49
|
+
end
|
50
|
+
|
51
|
+
attribute? :drone_charge_state do
|
52
|
+
# {"0":"not charging","1":"charging"}
|
53
|
+
attribute :state, Types::Integer.enum(0, 1)
|
54
|
+
|
55
|
+
# {"min":"0","max":"100"}
|
56
|
+
attribute :capacity_percent, Types::Integer.constrained(gteq: 0, lteq: 100)
|
57
|
+
end
|
58
|
+
attribute? :drone_in_dock, Types::Integer.enum(0, 1)
|
59
|
+
|
60
|
+
attribute? :electric_supply_voltage, Types::Integer
|
61
|
+
|
62
|
+
attribute? :flighttask_prepare_capacity, Types::Integer
|
63
|
+
attribute? :flighttask_step_code, Types::Integer
|
64
|
+
|
65
|
+
attribute? :job_number, Types::Integer
|
66
|
+
|
67
|
+
attribute? :latitude, Types::Latitude
|
68
|
+
attribute? :longitude, Types::Longitude
|
69
|
+
|
70
|
+
attribute? :maintain_status do
|
71
|
+
attribute :maintain_status_array, Types::Array do
|
72
|
+
# {"0":"No maintenance","1":"Under maintenance"}
|
73
|
+
attribute :state, Types::Integer
|
74
|
+
|
75
|
+
# {"0":"No maintenance","1":"Drone basic maintenance","2":"Drone routine maintenance","3":"Drone deep maintenance","17":"Dock maintenance"}
|
76
|
+
attribute :last_maintain_type, Types::Integer
|
77
|
+
|
78
|
+
attribute :last_maintain_time, Types::Integer
|
79
|
+
attribute :last_maintain_work_sorties, Types::Integer
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
attribute? :media_file_detail do
|
84
|
+
attribute :remain_upload, Types::Integer
|
85
|
+
end
|
86
|
+
|
87
|
+
attribute? :network_state do
|
88
|
+
attribute :type, Types::Integer
|
89
|
+
attribute :quality, Types::Integer
|
90
|
+
attribute :rate, Types::JSON::Decimal
|
91
|
+
end
|
92
|
+
|
93
|
+
attribute? :sdr do
|
94
|
+
attribute :down_quality, Types::Integer
|
95
|
+
attribute :frequency_band, Types::JSON::Decimal
|
96
|
+
attribute :up_quality, Types::Integer
|
97
|
+
end
|
98
|
+
|
99
|
+
attribute? :wireless_link do
|
100
|
+
attribute :_4g_freq_band, Types::JSON::Decimal
|
101
|
+
attribute :_4g_gnd_quality, Types::Integer
|
102
|
+
attribute :_4g_link_state, Types::Integer
|
103
|
+
attribute :_4g_quality, Types::Integer
|
104
|
+
attribute :_4g_uav_quality, Types::Integer
|
105
|
+
attribute :dongle_number, Types::Integer
|
106
|
+
attribute :link_workmode, Types::Integer
|
107
|
+
attribute :sdr_freq_band, Types::JSON::Decimal
|
108
|
+
attribute :sdr_link_state, Types::Integer
|
109
|
+
attribute :sdr_quality, Types::Integer
|
110
|
+
end
|
111
|
+
|
112
|
+
attribute? :working_current, Types::Integer
|
113
|
+
attribute? :working_voltage, Types::Integer
|
114
|
+
|
115
|
+
# Weather
|
116
|
+
|
117
|
+
attribute? :height, Types::JSON::Decimal
|
118
|
+
attribute? :humidity, Types::Integer
|
119
|
+
attribute? :rainfall, Types::JSON::Decimal
|
120
|
+
attribute? :temperature, Types::Temperature
|
121
|
+
attribute? :wind_speed, Types::JSON::Decimal
|
122
|
+
attribute? :environment_temperature, Types::Temperature
|
123
|
+
|
124
|
+
attribute? :alternate_land_point do
|
125
|
+
attribute :latitude, Types::Latitude
|
126
|
+
attribute :longitude, Types::Longitude
|
127
|
+
attribute :height, Types::JSON::Decimal
|
128
|
+
attribute :safe_land_height, Types::JSON::Decimal
|
129
|
+
attribute :is_configured, Types::Integer.enum(0, 1)
|
130
|
+
end
|
131
|
+
|
132
|
+
attribute? :first_power_on, Types::Integer
|
133
|
+
|
134
|
+
attribute? :position_state do
|
135
|
+
# {"0":"Not calibrated","1":"Calibrated"}
|
136
|
+
attribute :is_calibration, Types::Integer.enum(0, 1)
|
137
|
+
|
138
|
+
# {"0":"Not start","1":"fixing","2":"fix successfully","3":"fix failed"}
|
139
|
+
attribute :is_fixed, Types::Integer.enum(0, 1, 2, 3)
|
140
|
+
|
141
|
+
attribute :quality, Types::Integer.constrained(gteq: 0, lteq: 5)
|
142
|
+
attribute :gps_number, Types::Integer
|
143
|
+
attribute :rtk_number, Types::Integer
|
144
|
+
end
|
145
|
+
|
146
|
+
attribute? :storage do
|
147
|
+
attribute :total, Types::Integer
|
148
|
+
attribute :used, Types::Integer
|
149
|
+
end
|
150
|
+
|
151
|
+
# {"0":"Idle","1":"On-site debugging","2":"Remote debugging","3":"Firmware upgrading","4":"Working"}
|
152
|
+
attribute? :mode_code, Types::Integer.enum(0, 1, 2, 3, 4)
|
153
|
+
|
154
|
+
attribute? :air_conditioner do
|
155
|
+
# {"0":"Idle mode (No cooling, heating, and dehumidification)","1":"Cooling mode","2":"Heating mode","3":"Dehumidification mode","4":"Cooling exit mode","5":"Heating exit mode","6":"Dehumidification exit mode","7":"Cooling preparation mode","8":"Heating preparation mode","9":"Dehumidification preparation mode"}
|
156
|
+
attribute :air_conditioner_state, Types::Integer.enum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
157
|
+
attribute :switch_time, Types::Integer
|
158
|
+
end
|
159
|
+
|
160
|
+
# {"0":"Closed","1":"Opened"}
|
161
|
+
attribute? :alarm_state, Types::Integer.enum(0, 1)
|
162
|
+
|
163
|
+
# {"1":"Planned storage strategy of battery","2":"Emergency storage strategy of battery"}
|
164
|
+
attribute? :battery_store_mode, Types::Integer.enum(1, 2)
|
165
|
+
|
166
|
+
# {"0":"Closed","1":"Opened","2":"Half-open","3":"Cover state is abnormal"}
|
167
|
+
attribute? :cover_state, Types::Integer.enum(0, 1, 2, 3)
|
168
|
+
|
169
|
+
# {"0":"Closed","1":"Opened"}
|
170
|
+
attribute? :emergency_stop_state, Types::Integer.enum(0, 1)
|
171
|
+
|
172
|
+
# {"0":"Closed","1":"Opened","2":"Half-open","3":"Putter state is abnormal"}
|
173
|
+
attribute? :putter_state, Types::Integer.enum(0, 1, 2, 3)
|
174
|
+
|
175
|
+
# {"0":"Closed","1":"Opened"}
|
176
|
+
attribute? :supplement_light_state, Types::Integer.enum(0, 1)
|
177
|
+
|
178
|
+
attribute? :sub_device do
|
179
|
+
attribute? :device_sn, Types::String
|
180
|
+
attribute? :device_model_key, Types::String
|
181
|
+
|
182
|
+
# {"0":"Offline","1":"Online"}
|
183
|
+
attribute :device_online_status, Types::Integer.enum(0, 1)
|
184
|
+
|
185
|
+
# {"0":"Not paired","1":"Paired"}
|
186
|
+
attribute :device_paired, Types::Integer.enum(0, 1)
|
187
|
+
end
|
188
|
+
|
189
|
+
def drone_in_dock?
|
190
|
+
drone_in_dock == 1
|
191
|
+
end
|
192
|
+
|
193
|
+
def environment_temperature?
|
194
|
+
environment_temperature && !Types::InvalidTemperature.valid?(environment_temperature)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -3,19 +3,42 @@
|
|
3
3
|
module DjiMqttConnect
|
4
4
|
module Thing::Product
|
5
5
|
class DroneOsdMessage < OsdMessage
|
6
|
+
STABLE_DATA_ATTRIBUTES = %i[
|
7
|
+
mode_code
|
8
|
+
gear
|
9
|
+
horizontal_speed
|
10
|
+
vertical_speed
|
11
|
+
latitude
|
12
|
+
longitude
|
13
|
+
height
|
14
|
+
elevation
|
15
|
+
attitude_head
|
16
|
+
attitude_pitch
|
17
|
+
attitude_roll
|
18
|
+
home_distance
|
19
|
+
wind_direction
|
20
|
+
wind_speed
|
21
|
+
]
|
22
|
+
|
6
23
|
attribute :data do
|
24
|
+
include Mixins::LatitudeConditional
|
25
|
+
include Mixins::LongitudeConditional
|
26
|
+
|
7
27
|
attribute :attitude_head, Types::JSON::Decimal
|
8
28
|
attribute :attitude_pitch, Types::JSON::Decimal
|
9
29
|
attribute :attitude_roll, Types::JSON::Decimal
|
10
30
|
attribute :battery do
|
11
31
|
attribute :batteries, Types::Array do
|
32
|
+
include Mixins::TemperatureConditional
|
33
|
+
|
12
34
|
attribute :capacity_percent, Types::Integer
|
13
35
|
attribute :firmware_version, Types::String
|
36
|
+
attribute? :high_voltage_storage_days, Types::Integer
|
14
37
|
attribute :index, Types::Integer
|
15
38
|
attribute :loop_times, Types::Integer
|
16
39
|
attribute :sn, Types::String
|
17
40
|
attribute :sub_type, Types::DeviceSubType
|
18
|
-
attribute :temperature, Types::
|
41
|
+
attribute :temperature, Types::Temperature
|
19
42
|
attribute :type, Types::DeviceType
|
20
43
|
attribute :voltage, Types::Integer
|
21
44
|
end
|
@@ -24,15 +47,21 @@ module DjiMqttConnect
|
|
24
47
|
attribute :remain_flight_time, Types::Integer
|
25
48
|
attribute :return_home_power, Types::Integer
|
26
49
|
end
|
27
|
-
attribute :distance_limit_status do
|
50
|
+
attribute? :distance_limit_status do
|
28
51
|
attribute :distance_limit, Types::Integer
|
29
|
-
|
52
|
+
|
53
|
+
# {"0":"Not reached","1":"Reached"}
|
54
|
+
attribute? :is_near_distance_limit, Types::Integer.enum(0, 1)
|
55
|
+
|
56
|
+
# {"0":"Not set","1":"Have been set"}
|
57
|
+
attribute :state, Types::Integer.enum(0, 1)
|
30
58
|
end
|
31
59
|
attribute :elevation, Types::JSON::Decimal
|
60
|
+
|
32
61
|
attribute :firmware_version, Types::String
|
33
62
|
attribute :gear, Types::Integer
|
34
63
|
attribute :height, Types::JSON::Decimal
|
35
|
-
attribute :height_limit, Types::JSON::Decimal
|
64
|
+
attribute? :height_limit, Types::JSON::Decimal
|
36
65
|
attribute :home_distance, Types::JSON::Decimal
|
37
66
|
attribute :horizontal_speed, Types::JSON::Decimal
|
38
67
|
attribute :latitude, Types::Latitude
|
@@ -44,24 +73,99 @@ module DjiMqttConnect
|
|
44
73
|
attribute :quality, Types::Integer
|
45
74
|
attribute :rtk_number, Types::Integer
|
46
75
|
end
|
47
|
-
attribute :storage do
|
76
|
+
attribute? :storage do
|
48
77
|
attribute :total, Types::Integer
|
49
78
|
attribute :used, Types::Integer
|
50
79
|
end
|
51
80
|
attribute :total_flight_distance, Types::JSON::Decimal
|
52
81
|
attribute :total_flight_time, Types::JSON::Decimal
|
53
|
-
attribute :track_id, Types::String
|
82
|
+
attribute? :track_id, Types::String
|
54
83
|
attribute :vertical_speed, Types::JSON::Decimal
|
55
84
|
attribute :wind_direction, Types::JSON::Decimal
|
56
85
|
attribute :wind_speed, Types::JSON::Decimal
|
57
86
|
|
58
|
-
|
59
|
-
|
87
|
+
# M30 Fields
|
88
|
+
|
89
|
+
attribute? :cameras, Types::Array do
|
90
|
+
# {"0":"Photo taking","1":"Recording"}
|
91
|
+
attribute :camera_mode, Types::Integer.enum(0, 1)
|
92
|
+
|
93
|
+
# {"min":2,"max":20}
|
94
|
+
attribute :ir_zoom_factor, Types::JSON::Decimal
|
95
|
+
|
96
|
+
attribute :liveview_world_region do
|
97
|
+
attribute :bottom, Types::JSON::Decimal.constrained(gteq: 0, lteq: 1)
|
98
|
+
attribute :left, Types::JSON::Decimal.constrained(gteq: 0, lteq: 1)
|
99
|
+
attribute :right, Types::JSON::Decimal.constrained(gteq: 0, lteq: 1)
|
100
|
+
attribute :top, Types::JSON::Decimal.constrained(gteq: 0, lteq: 1)
|
101
|
+
end
|
102
|
+
|
103
|
+
attribute :payload_index, Types::String
|
104
|
+
|
105
|
+
# {"0":"Idle","1":"Taking photo"}
|
106
|
+
attribute :photo_state, Types::Integer.enum(0, 1)
|
107
|
+
|
108
|
+
# {"0":"Idle","1":"Recording"}
|
109
|
+
attribute :recording_state, Types::Integer.enum(0, 1)
|
110
|
+
|
111
|
+
# {"unit":"s"}
|
112
|
+
attribute :record_time, Types::Integer
|
113
|
+
|
114
|
+
attribute :remain_photo_num, Types::Integer
|
115
|
+
|
116
|
+
# {"unit":"s"}
|
117
|
+
attribute :remain_record_duration, Types::Integer
|
118
|
+
|
119
|
+
# {"min":2,"max":200}
|
120
|
+
attribute :zoom_factor, Types::JSON::Decimal
|
60
121
|
end
|
61
122
|
|
62
|
-
|
63
|
-
|
123
|
+
# {"0":"Continue to execute the wayline task","1":"Exit the wayline task, and execute the remote controller out of control action."}
|
124
|
+
attribute? :exit_wayline_when_rc_lost, Types::Integer.enum(0, 1)
|
125
|
+
|
126
|
+
attribute? :is_near_area_limit, Types::Integer.enum(0, 1)
|
127
|
+
|
128
|
+
# {"0":"Not reached","1":"Reached"}
|
129
|
+
attribute? :is_near_height_limit, Types::Integer.enum(0, 1)
|
130
|
+
|
131
|
+
attribute? :maintain_status do
|
132
|
+
attribute :maintain_status_array, Types::Array do
|
133
|
+
attribute :last_maintain_flight_sorties, Types::Integer
|
134
|
+
|
135
|
+
# {"unit":"hour"}
|
136
|
+
attribute :last_maintain_flight_time, Types::Integer
|
137
|
+
|
138
|
+
attribute :last_maintain_time, Types::Integer
|
139
|
+
|
140
|
+
# {"0":"No maintenance","1":"Drone basic maintenance","2":"Drone routine maintenance","3":"Drone deep maintenance","17":"Dock maintenance"}
|
141
|
+
attribute :last_maintain_type, Types::Integer.enum(0, 1, 2, 3, 17)
|
142
|
+
|
143
|
+
# {"0":"No maintenance","1":"Under maintenance"}
|
144
|
+
attribute :state, Types::Integer.enum(0, 1)
|
145
|
+
end
|
64
146
|
end
|
147
|
+
|
148
|
+
# {"0":"Off","1":"On"}
|
149
|
+
attribute? :night_lights_state, Types::Integer.enum(0, 1)
|
150
|
+
|
151
|
+
attribute? :obstacle_avoidance do
|
152
|
+
# {"0":"Close","1":"Open"}
|
153
|
+
attribute :horizon, Types::Integer.enum(0, 1)
|
154
|
+
|
155
|
+
# {"0":"Close","1":"Open"}
|
156
|
+
attribute :upside, Types::Integer.enum(0, 1)
|
157
|
+
|
158
|
+
# {"0":"Close","1":"Open"}
|
159
|
+
attribute :downside, Types::Integer.enum(0, 1)
|
160
|
+
end
|
161
|
+
|
162
|
+
# {"0":"Hovering","1":"Landing","2":"Returning to home"}
|
163
|
+
attribute? :rc_lost_action, Types::Integer.enum(0, 1, 2)
|
164
|
+
|
165
|
+
# {"unit":"m","min":20,"max":500}
|
166
|
+
attribute? :rth_altitude, Types::Integer
|
167
|
+
|
168
|
+
attribute? :total_flight_sorties, Types::Integer
|
65
169
|
end
|
66
170
|
end
|
67
171
|
end
|
@@ -4,6 +4,9 @@ module DjiMqttConnect
|
|
4
4
|
module Thing::Product
|
5
5
|
class RemoteOsdMessage < OsdMessage
|
6
6
|
attribute :data do
|
7
|
+
include Mixins::LatitudeConditional
|
8
|
+
include Mixins::LongitudeConditional
|
9
|
+
|
7
10
|
attribute :capacity_percent, Types::Integer
|
8
11
|
attribute :latitude, Types::Latitude
|
9
12
|
attribute :live_status do
|
@@ -14,7 +17,7 @@ module DjiMqttConnect
|
|
14
17
|
end
|
15
18
|
attribute :longitude, Types::Longitude
|
16
19
|
attribute :transmission_signal_quality, Types::Integer
|
17
|
-
attribute :wireless_link do
|
20
|
+
attribute? :wireless_link do
|
18
21
|
attribute :_4g_freq_band, Types::JSON::Decimal
|
19
22
|
attribute :_4g_gnd_quality, Types::Integer
|
20
23
|
attribute :_4g_link_state, Types::Integer
|
@@ -31,14 +34,6 @@ module DjiMqttConnect
|
|
31
34
|
attribute :frequency_band, Types::Integer
|
32
35
|
attribute :upward_quality, Types::Integer
|
33
36
|
end
|
34
|
-
|
35
|
-
def latitude?
|
36
|
-
!Types::NullInteger.valid?(latitude)
|
37
|
-
end
|
38
|
-
|
39
|
-
def longitude?
|
40
|
-
!Types::NullInteger.valid?(longitude)
|
41
|
-
end
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Thing::Product
|
5
|
+
# https://developer.dji.com/doc/cloud-api-tutorial/en/server-api-reference/mqtt/thing-model/gateway/dock/file.html#obtain-upload-temporary-credentials
|
6
|
+
class StorageConfigGetRequestsMessage < RequestsMessage
|
7
|
+
attribute :_method, Types::String.enum("storage_config_get")
|
8
|
+
|
9
|
+
attribute :data do
|
10
|
+
attribute :module, Types::Integer.enum(0)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Thing::Product
|
5
|
+
# https://developer.dji.com/doc/cloud-api-tutorial/en/server-api-reference/mqtt/thing-model/gateway/dock/file.html#obtain-upload-temporary-credentials
|
6
|
+
class StorageConfigGetRequestsReplyMessage < Message
|
7
|
+
def self.build_for(storage_config_get_message, output:, result: 0)
|
8
|
+
new(
|
9
|
+
_method: storage_config_get_message._method,
|
10
|
+
tid: storage_config_get_message.tid,
|
11
|
+
bid: storage_config_get_message.bid,
|
12
|
+
timestamp: Time.now.strftime("%s%L").to_i,
|
13
|
+
data: {
|
14
|
+
result: result,
|
15
|
+
output: output
|
16
|
+
}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
attribute :tid, Types::UUID
|
21
|
+
attribute :bid, Types::UUID
|
22
|
+
attribute :timestamp, Types::Timestamp
|
23
|
+
|
24
|
+
attribute :_method, Types::String.enum("storage_config_get")
|
25
|
+
|
26
|
+
attribute :data do
|
27
|
+
attribute :result, Types::Integer
|
28
|
+
attribute :output do
|
29
|
+
attribute :bucket, Types::String
|
30
|
+
attribute :credentials do
|
31
|
+
attribute :access_key_id, Types::String
|
32
|
+
attribute :access_key_secret, Types::String
|
33
|
+
attribute :expire, Types::Integer
|
34
|
+
attribute :security_token, Types::String
|
35
|
+
end
|
36
|
+
attribute :endpoint, Types::String
|
37
|
+
attribute :provider, Types::String
|
38
|
+
attribute :region, Types::String
|
39
|
+
attribute :object_key_prefix, Types::String
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Mixins
|
5
|
+
# Provides a conditional that returns false for nil for invalid latitude values
|
6
|
+
module LatitudeConditional
|
7
|
+
def latitude?
|
8
|
+
latitude && !Types::NullInteger.valid?(latitude)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Mixins
|
5
|
+
# Provides a conditional that returns false for nil for invalid longitude values
|
6
|
+
module LongitudeConditional
|
7
|
+
def longitude?
|
8
|
+
longitude && !Types::NullInteger.valid?(longitude)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DjiMqttConnect
|
4
|
+
module Mixins
|
5
|
+
# Provides a conditional that returns false for nil or excessive values (invalid data)
|
6
|
+
module TemperatureConditional
|
7
|
+
def temperature?
|
8
|
+
temperature && !Types::InvalidTemperature.valid?(temperature)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -16,7 +16,14 @@ module DjiMqttConnect
|
|
16
16
|
message = status_marshal.load(raw_message)
|
17
17
|
|
18
18
|
logger.info("Received #{message} from #{device_identifier}")
|
19
|
-
|
19
|
+
|
20
|
+
if message.instance_of?(StatusMessage)
|
21
|
+
# Broadcast an unsupported message event
|
22
|
+
broadcast(:unsupported_message, topic, raw_message)
|
23
|
+
else
|
24
|
+
# Build event name and broadcast (i.e. ::UpdateTopoStatusMessage => update_topo)
|
25
|
+
broadcast(message._method.to_sym, device_identifier, message)
|
26
|
+
end
|
20
27
|
rescue ParseError => error
|
21
28
|
broadcast(:parse_error, error, topic, raw_message)
|
22
29
|
end
|
@@ -17,9 +17,14 @@ module DjiMqttConnect
|
|
17
17
|
|
18
18
|
logger.info("Received #{message} from #{device_identifier}")
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if message.instance_of?(OsdMessage)
|
21
|
+
# Broadcast an unsupported message event
|
22
|
+
broadcast(:unsupported_message, topic, raw_message)
|
23
|
+
else
|
24
|
+
# Build event name and broadcast (i.e. ::RemoteOsdMessage => remote_osd_update)
|
25
|
+
event_name = message.class.name.demodulize.sub(/Message\z/, "Update").underscore.to_sym
|
26
|
+
broadcast(event_name, device_identifier, message)
|
27
|
+
end
|
23
28
|
rescue ParseError => error
|
24
29
|
broadcast(:parse_error, error, topic, raw_message)
|
25
30
|
end
|
@@ -16,7 +16,14 @@ module DjiMqttConnect
|
|
16
16
|
message = requests_marshal.load(raw_message)
|
17
17
|
|
18
18
|
logger.info("Received #{message} from #{gateway_sn}")
|
19
|
-
|
19
|
+
|
20
|
+
if message.instance_of?(RequestsMessage)
|
21
|
+
# Broadcast an unsupported message event
|
22
|
+
broadcast(:unsupported_message, topic, raw_message)
|
23
|
+
else
|
24
|
+
# Build event name and broadcast (i.e. ::ConfigRequestsMessage => config)
|
25
|
+
broadcast(message._method.to_sym, gateway_sn, message)
|
26
|
+
end
|
20
27
|
rescue ParseError => error
|
21
28
|
broadcast(:parse_error, error, topic, raw_message)
|
22
29
|
end
|
@@ -24,5 +24,8 @@ module DjiMqttConnect
|
|
24
24
|
DeviceDomain = Types::Coercible::Integer # dock returns this value as string
|
25
25
|
DeviceType = Types::Integer
|
26
26
|
DeviceSubType = Types::Integer
|
27
|
+
|
28
|
+
InvalidTemperature = Types::Integer.constrained(gteq: 1300) # maximum temp of k-type thermocouple, errors are usually 30K+
|
29
|
+
Temperature = InvalidTemperature | Types::JSON::Decimal
|
27
30
|
end
|
28
31
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/keys"
|
4
|
+
|
5
|
+
module DjiMqttConnect
|
6
|
+
module Utils
|
7
|
+
# Sanitizes MQTT messages to remove any personally-identifiable information
|
8
|
+
class MessageSanitizer
|
9
|
+
DEFAULT_LATITUDE = "-33.865143"
|
10
|
+
DEFAULT_LONGITUDE = "151.209900"
|
11
|
+
|
12
|
+
SECRET_KEYS = %w[app_id app_key app_license callsign device_secret nonce]
|
13
|
+
|
14
|
+
SERIAL_NUMBER_KEYS = %w[device_sn gateway sn]
|
15
|
+
|
16
|
+
TOPIC_REGEX = /\A.+\/.+\/(.+)\/.+/
|
17
|
+
|
18
|
+
def initialize(latitude: DEFAULT_LATITUDE, longitude: DEFAULT_LONGITUDE, secret_keys: SECRET_KEYS, serial_number_keys: SERIAL_NUMBER_KEYS, serial_number_aliases: {})
|
19
|
+
@latitude = latitude
|
20
|
+
@longitude = longitude
|
21
|
+
|
22
|
+
@secret_keys = secret_keys
|
23
|
+
@serial_number_keys = serial_number_keys
|
24
|
+
|
25
|
+
serial_aliases = serial_number_aliases.stringify_keys
|
26
|
+
@serial_number_generator = Hash.new do |h, k|
|
27
|
+
return "" if k.length == 0
|
28
|
+
h[k] = serial_aliases.fetch(k) { sprintf("%s%02d", "SERIAL", h.length + 1) }
|
29
|
+
end
|
30
|
+
serial_aliases.keys.each { |k| serial_number_generator[k] }
|
31
|
+
end
|
32
|
+
|
33
|
+
def sanitize_topic(topic)
|
34
|
+
topic.sub(TOPIC_REGEX) { |topic_match| topic_match.sub($1, serial_number_generator[$1]) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def sanitize_message(message)
|
38
|
+
sanitized_message = sanitize_object(message)
|
39
|
+
strip_serial_numbers(sanitized_message)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :latitude, :longitude
|
45
|
+
attr_reader :secret_keys, :serial_number_keys, :serial_number_generator
|
46
|
+
|
47
|
+
def sanitize_object(object)
|
48
|
+
case object
|
49
|
+
when Hash
|
50
|
+
serial_number_keys.each do |key|
|
51
|
+
if object.key?(key) && object[key].length > 0
|
52
|
+
object[key] = serial_number_generator[object[key]]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
secret_keys.each do |key|
|
56
|
+
object[key] = key.upcase if object.key?(key)
|
57
|
+
end
|
58
|
+
if object.key?("latitude")
|
59
|
+
obj_latitude = object["latitude"]
|
60
|
+
object["latitude"] = (obj_latitude.is_a?(Integer) && obj_latitude.zero?) ? 0 : latitude
|
61
|
+
end
|
62
|
+
if object.key?("longitude")
|
63
|
+
obj_longitude = object["longitude"]
|
64
|
+
object["longitude"] = (obj_longitude.is_a?(Integer) && obj_longitude.zero?) ? 0 : longitude
|
65
|
+
end
|
66
|
+
|
67
|
+
object.transform_values { |v| sanitize_object(v) }
|
68
|
+
when Array
|
69
|
+
object.map { |v| sanitize_object(v) }
|
70
|
+
else
|
71
|
+
object
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Strips known serial numbers from an object
|
76
|
+
def strip_serial_numbers(object)
|
77
|
+
case object
|
78
|
+
when Hash
|
79
|
+
object.transform_values { |v| strip_serial_numbers(v) }
|
80
|
+
when Array
|
81
|
+
object.map { |v| strip_serial_numbers(v) }
|
82
|
+
when String
|
83
|
+
object.split("-").map { |s| serial_number_generator.fetch(s, s) }.join("-")
|
84
|
+
else
|
85
|
+
object
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/wrap"
|
4
|
+
|
5
|
+
module DjiMqttConnect
|
6
|
+
module Utils
|
7
|
+
# Generates a simple message schema from a mqtt message
|
8
|
+
#
|
9
|
+
# Schema is simply the class name of any element that is not a Hash or Array
|
10
|
+
class MessageSchemaGenerator
|
11
|
+
def initialize(retain_attributes: [])
|
12
|
+
@retain_attributes = Array.wrap(retain_attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate(message)
|
16
|
+
case message
|
17
|
+
when Hash
|
18
|
+
retain_values = retain_attributes.each_with_object({}) do |attribute, hash|
|
19
|
+
next unless message.key?(attribute)
|
20
|
+
|
21
|
+
attribute_value = message[attribute]
|
22
|
+
next if attribute_value.is_a?(Hash) || attribute_value.is_a?(Array)
|
23
|
+
|
24
|
+
hash[attribute] = "#{attribute_value.class.name}:#{attribute_value}"
|
25
|
+
end
|
26
|
+
message.transform_values { |v| generate(v) }.merge(retain_values)
|
27
|
+
when Array
|
28
|
+
message.map { |v| generate(v) }
|
29
|
+
else
|
30
|
+
message.class.name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :retain_attributes
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
|
5
|
+
module DjiMqttConnect
|
6
|
+
module Utils
|
7
|
+
# Generates anonymised fixture data from MQTTX JSON Export Dumps
|
8
|
+
class MqttxFixtureDataGenerator
|
9
|
+
def self.from_file(file_path, **sanitizer_args)
|
10
|
+
raw_data = File.read(file_path)
|
11
|
+
new JSON.parse(raw_data), message_sanitizer: MessageSanitizer.new(**sanitizer_args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(mqttx_data, message_sanitizer:)
|
15
|
+
@mqttx_data = mqttx_data
|
16
|
+
@message_sanitizer = message_sanitizer
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generates fixture data using ALL found messages in the MQTTX export
|
20
|
+
#
|
21
|
+
# # Output is:
|
22
|
+
# <topic 1>: <message 1 as json>
|
23
|
+
# <topic 1>: <message 2 as json>
|
24
|
+
def generate
|
25
|
+
extracted_messages.each_with_object(StringIO.new) do |(topic, message), builder|
|
26
|
+
builder.puts [topic, message.to_json].join(": ")
|
27
|
+
end.string
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generates fixture data using unique found messages in the MQTTX export
|
31
|
+
#
|
32
|
+
# A message is considered unique if the topic and schema does not match any other messages
|
33
|
+
#
|
34
|
+
# # Output is:
|
35
|
+
# <topic 1>: <message 1 as json>
|
36
|
+
def generate_unique(**schema_generator_args)
|
37
|
+
message_schema_generator = MessageSchemaGenerator.new(**schema_generator_args)
|
38
|
+
|
39
|
+
unique_messages = extracted_messages.each_with_object({}) do |(topic, message), found|
|
40
|
+
message_schema = message_schema_generator.generate(message)
|
41
|
+
found[[topic, message_schema]] ||= [topic, message]
|
42
|
+
end.values
|
43
|
+
|
44
|
+
unique_messages.each_with_object(StringIO.new) do |(topic, message), builder|
|
45
|
+
builder.puts [topic, message.to_json].join(": ")
|
46
|
+
end.string
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :mqttx_data, :message_sanitizer
|
52
|
+
|
53
|
+
def extracted_messages
|
54
|
+
return enum_for(:extracted_messages) unless block_given?
|
55
|
+
|
56
|
+
mqttx_data.each do |client_output|
|
57
|
+
client_messages = client_output["messages"]
|
58
|
+
|
59
|
+
client_messages.each do |message|
|
60
|
+
message_topic = message_sanitizer.sanitize_topic(message.fetch("topic"))
|
61
|
+
message_payload = JSON.parse message.fetch("payload")
|
62
|
+
yield(message_topic, message_sanitizer.sanitize_message(message_payload))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/dji_mqtt_connect.rb
CHANGED
@@ -34,15 +34,20 @@ module DjiMqttConnect
|
|
34
34
|
module Product
|
35
35
|
# Messages
|
36
36
|
autoload :OsdMessage, "dji_mqtt_connect/messages/thing/product/osd_message"
|
37
|
+
autoload :DockOsdMessage, "dji_mqtt_connect/messages/thing/product/dock_osd_message"
|
37
38
|
autoload :DroneOsdMessage, "dji_mqtt_connect/messages/thing/product/drone_osd_message"
|
38
39
|
autoload :RemoteOsdMessage, "dji_mqtt_connect/messages/thing/product/remote_osd_message"
|
39
40
|
|
40
41
|
autoload :AirportBindStatusRequestsMessage, "dji_mqtt_connect/messages/thing/product/airport_bind_status_requests_message"
|
42
|
+
autoload :AirportOrganizationGetRequestsMessage, "dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_message"
|
41
43
|
autoload :ConfigRequestsMessage, "dji_mqtt_connect/messages/thing/product/config_requests_message"
|
44
|
+
autoload :StorageConfigGetRequestsMessage, "dji_mqtt_connect/messages/thing/product/storage_config_get_requests_message"
|
42
45
|
autoload :RequestsMessage, "dji_mqtt_connect/messages/thing/product/requests_message"
|
43
46
|
|
44
47
|
autoload :AirportBindStatusRequestsReplyMessage, "dji_mqtt_connect/messages/thing/product/airport_bind_status_requests_reply_message"
|
48
|
+
autoload :AirportOrganizationGetRequestsReplyMessage, "dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_reply_message"
|
45
49
|
autoload :ConfigRequestsReplyMessage, "dji_mqtt_connect/messages/thing/product/config_requests_reply_message"
|
50
|
+
autoload :StorageConfigGetRequestsReplyMessage, "dji_mqtt_connect/messages/thing/product/storage_config_get_requests_reply_message"
|
46
51
|
|
47
52
|
# Topics
|
48
53
|
autoload :OsdTopicRepository, "dji_mqtt_connect/topics/thing/product/osd"
|
@@ -57,9 +62,19 @@ module DjiMqttConnect
|
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
65
|
+
# Mixinx
|
66
|
+
module Mixins
|
67
|
+
autoload :LatitudeConditional, "dji_mqtt_connect/mixins/latitude_conditional"
|
68
|
+
autoload :LongitudeConditional, "dji_mqtt_connect/mixins/longitude_conditional"
|
69
|
+
autoload :TemperatureConditional, "dji_mqtt_connect/mixins/temperature_conditional"
|
70
|
+
end
|
71
|
+
|
60
72
|
# Utils
|
61
73
|
module Utils
|
62
74
|
autoload :MessageParsing, "dji_mqtt_connect/utils/message_parsing"
|
75
|
+
autoload :MessageSanitizer, "dji_mqtt_connect/utils/message_sanitizer"
|
76
|
+
autoload :MessageSchemaGenerator, "dji_mqtt_connect/utils/message_schema_generator"
|
77
|
+
autoload :MqttxFixtureDataGenerator, "dji_mqtt_connect/utils/mqttx_fixture_data_generator"
|
63
78
|
end
|
64
79
|
|
65
80
|
# Railties
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dji_mqtt_connect
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sphere Drones
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- Gemfile.lock
|
104
104
|
- README.md
|
105
105
|
- Rakefile
|
106
|
+
- dji_mqtt_connect.gemspec
|
106
107
|
- lib/dji_mqtt_connect.rb
|
107
108
|
- lib/dji_mqtt_connect/client.rb
|
108
109
|
- lib/dji_mqtt_connect/error.rb
|
@@ -119,12 +120,20 @@ files:
|
|
119
120
|
- lib/dji_mqtt_connect/messages/sys/product/update_topo_status_reply_message.rb
|
120
121
|
- lib/dji_mqtt_connect/messages/thing/product/airport_bind_status_requests_message.rb
|
121
122
|
- lib/dji_mqtt_connect/messages/thing/product/airport_bind_status_requests_reply_message.rb
|
123
|
+
- lib/dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_message.rb
|
124
|
+
- lib/dji_mqtt_connect/messages/thing/product/airport_organization_get_requests_reply_message.rb
|
122
125
|
- lib/dji_mqtt_connect/messages/thing/product/config_requests_message.rb
|
123
126
|
- lib/dji_mqtt_connect/messages/thing/product/config_requests_reply_message.rb
|
127
|
+
- lib/dji_mqtt_connect/messages/thing/product/dock_osd_message.rb
|
124
128
|
- lib/dji_mqtt_connect/messages/thing/product/drone_osd_message.rb
|
125
129
|
- lib/dji_mqtt_connect/messages/thing/product/osd_message.rb
|
126
130
|
- lib/dji_mqtt_connect/messages/thing/product/remote_osd_message.rb
|
127
131
|
- lib/dji_mqtt_connect/messages/thing/product/requests_message.rb
|
132
|
+
- lib/dji_mqtt_connect/messages/thing/product/storage_config_get_requests_message.rb
|
133
|
+
- lib/dji_mqtt_connect/messages/thing/product/storage_config_get_requests_reply_message.rb
|
134
|
+
- lib/dji_mqtt_connect/mixins/latitude_conditional.rb
|
135
|
+
- lib/dji_mqtt_connect/mixins/longitude_conditional.rb
|
136
|
+
- lib/dji_mqtt_connect/mixins/temperature_conditional.rb
|
128
137
|
- lib/dji_mqtt_connect/railtie.rb
|
129
138
|
- lib/dji_mqtt_connect/railties/message_serializer.rb
|
130
139
|
- lib/dji_mqtt_connect/topic_repository.rb
|
@@ -135,6 +144,9 @@ files:
|
|
135
144
|
- lib/dji_mqtt_connect/topics/thing/product/requests_reply.rb
|
136
145
|
- lib/dji_mqtt_connect/types.rb
|
137
146
|
- lib/dji_mqtt_connect/utils/message_parsing.rb
|
147
|
+
- lib/dji_mqtt_connect/utils/message_sanitizer.rb
|
148
|
+
- lib/dji_mqtt_connect/utils/message_schema_generator.rb
|
149
|
+
- lib/dji_mqtt_connect/utils/mqttx_fixture_data_generator.rb
|
138
150
|
- lib/dji_mqtt_connect/version.rb
|
139
151
|
- sig/dji_mqtt_connect.rbs
|
140
152
|
homepage: https://github.com/sphere-drones/dji_mqtt_connect
|