ents 2.3.2__py3-none-any.whl
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.
- ents/__init__.py +23 -0
- ents/calibrate/PingSMU.py +51 -0
- ents/calibrate/PingSPS.py +66 -0
- ents/calibrate/README.md +3 -0
- ents/calibrate/__init__.py +0 -0
- ents/calibrate/linear_regression.py +78 -0
- ents/calibrate/plots.py +83 -0
- ents/calibrate/recorder.py +678 -0
- ents/calibrate/requirements.txt +9 -0
- ents/cli.py +546 -0
- ents/config/README.md +123 -0
- ents/config/__init__.py +1 -0
- ents/config/adv_trace.py +56 -0
- ents/config/user_config.py +935 -0
- ents/proto/__init__.py +33 -0
- ents/proto/decode.py +106 -0
- ents/proto/encode.py +298 -0
- ents/proto/esp32.py +179 -0
- ents/proto/soil_power_sensor_pb2.py +72 -0
- ents/simulator/__init__.py +0 -0
- ents/simulator/node.py +161 -0
- ents-2.3.2.dist-info/METADATA +206 -0
- ents-2.3.2.dist-info/RECORD +26 -0
- ents-2.3.2.dist-info/WHEEL +4 -0
- ents-2.3.2.dist-info/entry_points.txt +5 -0
- ents-2.3.2.dist-info/licenses/LICENSE +21 -0
ents/proto/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .encode import (
|
|
2
|
+
encode_response,
|
|
3
|
+
encode_power_measurement,
|
|
4
|
+
encode_teros12_measurement,
|
|
5
|
+
encode_phytos31_measurement,
|
|
6
|
+
encode_teros21_measurement,
|
|
7
|
+
encode_user_configuration,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .decode import (
|
|
11
|
+
decode_response,
|
|
12
|
+
decode_measurement,
|
|
13
|
+
decode_user_configuration,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .esp32 import (
|
|
17
|
+
encode_esp32command,
|
|
18
|
+
decode_esp32command,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"encode_response",
|
|
23
|
+
"encode_power_measurement",
|
|
24
|
+
"encode_teros12_measurement",
|
|
25
|
+
"encode_phytos31_measurement",
|
|
26
|
+
"encode_teros21_measurement",
|
|
27
|
+
"decode_response",
|
|
28
|
+
"decode_measurement",
|
|
29
|
+
"encode_user_configuration",
|
|
30
|
+
"decode_user_configuration",
|
|
31
|
+
"encode_esp32command",
|
|
32
|
+
"decode_esp32command",
|
|
33
|
+
]
|
ents/proto/decode.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Module to decode soil power sensor messages"""
|
|
2
|
+
|
|
3
|
+
from google.protobuf.json_format import MessageToDict
|
|
4
|
+
|
|
5
|
+
from .soil_power_sensor_pb2 import Measurement, Response, UserConfiguration
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def decode_response(data: bytes):
|
|
9
|
+
"""Decodes a Response message.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
data: Byte array of Response message.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Returns the ResponseType.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
KeyError: Missing the resp field.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
resp = Response()
|
|
22
|
+
resp.ParseFromString(data)
|
|
23
|
+
|
|
24
|
+
if not resp.HasField("resp"):
|
|
25
|
+
raise KeyError("Missing response type")
|
|
26
|
+
|
|
27
|
+
return resp.resp
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def decode_measurement(data: bytes, raw: bool = True) -> dict:
|
|
31
|
+
"""Decodes a Measurement message
|
|
32
|
+
|
|
33
|
+
The data is decoded into a flat dictionary with the measurement type.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
data: Byte array of Measurement message.
|
|
37
|
+
raw: Flag to return raw or adjusted measurements
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Flat dictionary of values from the meta field, measurement field, and
|
|
41
|
+
the key "type" to indicate the type of measurement.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
KeyError: When the serialized data is missing a required field.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# parse data
|
|
48
|
+
meas = Measurement()
|
|
49
|
+
meas.ParseFromString(data)
|
|
50
|
+
|
|
51
|
+
# convert meta into dict
|
|
52
|
+
if not meas.HasField("meta"):
|
|
53
|
+
raise KeyError("Measurement missing metadata")
|
|
54
|
+
meta_dict = MessageToDict(meas.meta)
|
|
55
|
+
|
|
56
|
+
# decode measurement
|
|
57
|
+
if not meas.HasField("measurement"):
|
|
58
|
+
raise KeyError("Measurement missing data")
|
|
59
|
+
measurement_type = meas.WhichOneof("measurement")
|
|
60
|
+
measurement_dict = MessageToDict(getattr(meas, measurement_type))
|
|
61
|
+
|
|
62
|
+
# store measurement type
|
|
63
|
+
meta_dict["type"] = measurement_type
|
|
64
|
+
|
|
65
|
+
# store measurement data
|
|
66
|
+
meta_dict["data"] = measurement_dict
|
|
67
|
+
|
|
68
|
+
# process raw
|
|
69
|
+
if not raw:
|
|
70
|
+
# convert measurements to hPa, C, and %
|
|
71
|
+
if meta_dict["type"] == "bme280":
|
|
72
|
+
meta_dict["data"]["pressure"] /= 10.0
|
|
73
|
+
meta_dict["data"]["temperature"] /= 100.0
|
|
74
|
+
meta_dict["data"]["humidity"] /= 1000.0
|
|
75
|
+
|
|
76
|
+
# store measurement type
|
|
77
|
+
meta_dict["data_type"] = {}
|
|
78
|
+
for key, value in measurement_dict.items():
|
|
79
|
+
meta_dict["data_type"][key] = type(value)
|
|
80
|
+
return meta_dict
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def decode_user_configuration(data: bytes) -> dict:
|
|
84
|
+
"""Decodes a UserConfiguration message
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
data: Byte array of UserConfiguration message.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Dictionary of UserConfiguration values.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
KeyError: When the serialized data is missing a required field.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
user_config = UserConfiguration()
|
|
97
|
+
user_config.ParseFromString(data)
|
|
98
|
+
|
|
99
|
+
if user_config.cell_id == 0 or user_config.logger_id == 0:
|
|
100
|
+
raise KeyError("User configuration missing required fields")
|
|
101
|
+
|
|
102
|
+
user_config_dict = MessageToDict(
|
|
103
|
+
user_config, always_print_fields_with_no_presence=True
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return user_config_dict
|
ents/proto/encode.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""Module to encode soil power sensor protobuf messages
|
|
2
|
+
|
|
3
|
+
Encoding a response can be performed with
|
|
4
|
+
|
|
5
|
+
encode_response()
|
|
6
|
+
|
|
7
|
+
Each type of measurement has a corresponding encoding function as follows:
|
|
8
|
+
|
|
9
|
+
PowerMeasurement -> encode_power_measurement()
|
|
10
|
+
Teros12Measurement -> encode_teros12_measurement()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .soil_power_sensor_pb2 import (
|
|
14
|
+
Measurement,
|
|
15
|
+
Response,
|
|
16
|
+
UserConfiguration,
|
|
17
|
+
EnabledSensor,
|
|
18
|
+
Uploadmethod,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def encode_response(success: bool = True) -> bytes:
|
|
23
|
+
"""Encodes a Response message
|
|
24
|
+
|
|
25
|
+
The response indicates there was a successful upload, otherwise indicates an
|
|
26
|
+
error.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
success: Specifies whether to encode a success or failure.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Byte string of response message.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# format response
|
|
36
|
+
response = Response()
|
|
37
|
+
if success:
|
|
38
|
+
response.resp = Response.ResponseType.SUCCESS
|
|
39
|
+
else:
|
|
40
|
+
response.resp = Response.ResponseType.ERROR
|
|
41
|
+
|
|
42
|
+
# return encode message
|
|
43
|
+
return response.SerializeToString()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def encode_power_measurement(
|
|
47
|
+
ts: int, cell_id: int, logger_id: int, voltage: float, current: float
|
|
48
|
+
) -> bytes:
|
|
49
|
+
"""Encodes a PowerMeasurement within the Measurement message
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
ts: Timestamp in unix epochs
|
|
53
|
+
cell_id: Cell Id from Dirtviz
|
|
54
|
+
logger_id: Logger Id from Dirtviz
|
|
55
|
+
voltage: Voltage in V (Volts)
|
|
56
|
+
current: Current in A (Amps)
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Serialized Power measurement
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
meas = Measurement()
|
|
63
|
+
|
|
64
|
+
# metadata
|
|
65
|
+
meas.meta.ts = ts
|
|
66
|
+
meas.meta.cell_id = cell_id
|
|
67
|
+
meas.meta.logger_id = logger_id
|
|
68
|
+
|
|
69
|
+
# power
|
|
70
|
+
meas.power.voltage = voltage
|
|
71
|
+
meas.power.current = current
|
|
72
|
+
|
|
73
|
+
return meas.SerializeToString()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def encode_teros12_measurement(
|
|
77
|
+
ts: int,
|
|
78
|
+
cell_id: int,
|
|
79
|
+
logger_id: int,
|
|
80
|
+
vwc_raw: float,
|
|
81
|
+
vwc_adj: float,
|
|
82
|
+
temp: float,
|
|
83
|
+
ec: int,
|
|
84
|
+
) -> bytes:
|
|
85
|
+
"""Encodes a Teros12Measurment within the Measurement message
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
ts: Timestamp in unix epochs
|
|
89
|
+
cell_id: Cell Id from Dirtviz
|
|
90
|
+
logger_id: Logger Id from Dirtviz
|
|
91
|
+
vwc_raw: Raw volumetric water content from Teros12
|
|
92
|
+
vwc_adj: Volumetric water content from Teros12 with calibration applied
|
|
93
|
+
temp: Temperature in C
|
|
94
|
+
ec: Electrical conductivity
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Serialized Teros12 measurement
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
meas = Measurement()
|
|
101
|
+
|
|
102
|
+
# metadata
|
|
103
|
+
meas.meta.ts = ts
|
|
104
|
+
meas.meta.cell_id = cell_id
|
|
105
|
+
meas.meta.logger_id = logger_id
|
|
106
|
+
|
|
107
|
+
# teros12
|
|
108
|
+
meas.teros12.vwc_raw = vwc_raw
|
|
109
|
+
meas.teros12.vwc_adj = vwc_adj
|
|
110
|
+
meas.teros12.temp = temp
|
|
111
|
+
meas.teros12.ec = ec
|
|
112
|
+
|
|
113
|
+
return meas.SerializeToString()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def encode_phytos31_measurement(
|
|
117
|
+
ts: int, cell_id: int, logger_id: int, voltage: float, leaf_wetness: float
|
|
118
|
+
) -> bytes:
|
|
119
|
+
"""Encodes a Phytos31Measurement within the Measurement message
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
ts: Timestamp in unix epochs
|
|
123
|
+
cell_id: Cell Id from Dirtviz
|
|
124
|
+
logger_id: Logger Id from Dirtviz
|
|
125
|
+
voltage: Raw voltage reading
|
|
126
|
+
leaf_wetness: Calibrated leaf wetness
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Serialized Phytos31 measurement
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
meas = Measurement()
|
|
133
|
+
|
|
134
|
+
# metadata
|
|
135
|
+
meas.meta.ts = ts
|
|
136
|
+
meas.meta.cell_id = cell_id
|
|
137
|
+
meas.meta.logger_id = logger_id
|
|
138
|
+
|
|
139
|
+
# phytos31
|
|
140
|
+
meas.phytos31.voltage = voltage
|
|
141
|
+
meas.phytos31.leaf_wetness = leaf_wetness
|
|
142
|
+
return meas.SerializeToString()
|
|
143
|
+
|
|
144
|
+
return meas.SerializeToString()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def encode_bme280_measurement(
|
|
148
|
+
ts: int,
|
|
149
|
+
cell_id: int,
|
|
150
|
+
logger_id: int,
|
|
151
|
+
pressure: int,
|
|
152
|
+
temperature: int,
|
|
153
|
+
humidity: int,
|
|
154
|
+
):
|
|
155
|
+
"""Encodes a BME280Measurement within the Measurement message
|
|
156
|
+
|
|
157
|
+
The following raw values correspond to the following SI units
|
|
158
|
+
|
|
159
|
+
*Raw*
|
|
160
|
+
|
|
161
|
+
pressure: 98473
|
|
162
|
+
temperature: 2275
|
|
163
|
+
humidity: 43600
|
|
164
|
+
|
|
165
|
+
*SI Units*
|
|
166
|
+
|
|
167
|
+
pressure: 9847.3 hPa
|
|
168
|
+
temperature: 22.75 C
|
|
169
|
+
humidity: 43.600 %
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
ts: Timestamp in unix epochs
|
|
173
|
+
cell_id: Cell Id from Dirtviz
|
|
174
|
+
logger_id: Logger Id from Dirtviz
|
|
175
|
+
pressure: Ambient pressure
|
|
176
|
+
temperature: Ambient temperature
|
|
177
|
+
humidity: Relative humidity
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Serialized BME280 Measurement
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
meas = Measurement()
|
|
184
|
+
|
|
185
|
+
# metadata
|
|
186
|
+
meas.meta.ts = ts
|
|
187
|
+
meas.meta.cell_id = cell_id
|
|
188
|
+
meas.meta.logger_id = logger_id
|
|
189
|
+
|
|
190
|
+
# bme280
|
|
191
|
+
meas.bme280.pressure = pressure
|
|
192
|
+
meas.bme280.temperature = temperature
|
|
193
|
+
meas.bme280.humidity = humidity
|
|
194
|
+
|
|
195
|
+
return meas.SerializeToString()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def encode_teros21_measurement(
|
|
199
|
+
ts: int,
|
|
200
|
+
logger_id: int,
|
|
201
|
+
cell_id: int,
|
|
202
|
+
matric_pot: float,
|
|
203
|
+
temp: float,
|
|
204
|
+
) -> bytes:
|
|
205
|
+
|
|
206
|
+
meas = Measurement()
|
|
207
|
+
|
|
208
|
+
# metadata
|
|
209
|
+
meas.meta.ts = ts
|
|
210
|
+
meas.meta.cell_id = cell_id
|
|
211
|
+
meas.meta.logger_id = logger_id
|
|
212
|
+
|
|
213
|
+
meas.teros21.matric_pot = matric_pot
|
|
214
|
+
meas.teros21.temp = temp
|
|
215
|
+
|
|
216
|
+
return meas.SerializeToString()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def encode_user_configuration(
|
|
220
|
+
logger_id: int,
|
|
221
|
+
cell_id: int,
|
|
222
|
+
Upload_method: str,
|
|
223
|
+
Upload_interval: int,
|
|
224
|
+
Enabled_sensors: list,
|
|
225
|
+
Voltage_Slope: float,
|
|
226
|
+
Voltage_Offset: float,
|
|
227
|
+
Current_Slope: float,
|
|
228
|
+
Current_Offset: float,
|
|
229
|
+
WiFi_SSID: str,
|
|
230
|
+
WiFi_Password: str,
|
|
231
|
+
API_Endpoint_URL: str,
|
|
232
|
+
API_Endpoint_Port: int,
|
|
233
|
+
) -> bytes:
|
|
234
|
+
"""Encodes a UserConfiguration message
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
logger_id: ID of the logger.
|
|
238
|
+
cell_id: ID of the cell.
|
|
239
|
+
Upload_method: indicates whether LoRa or WiFi is used
|
|
240
|
+
Upload_interval: upload time in seconds
|
|
241
|
+
Enabled_sensors: used sensors
|
|
242
|
+
Voltage_Slope: Calibration slope for voltage.
|
|
243
|
+
Voltage_Offset: Calibration offset for voltage.
|
|
244
|
+
Current_Slope: Calibration slope for current.
|
|
245
|
+
Current_Offset: Calibration offset for current.
|
|
246
|
+
WiFi_SSID: WiFi SSID.
|
|
247
|
+
WiFi_Password: WiFi password.
|
|
248
|
+
API_Endpoint_URL
|
|
249
|
+
API_Endpoint_Port
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Serialized UserConfiguration message
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
user_config = UserConfiguration()
|
|
256
|
+
|
|
257
|
+
user_config.logger_id = logger_id
|
|
258
|
+
user_config.cell_id = cell_id
|
|
259
|
+
user_config.Upload_method = Upload_method
|
|
260
|
+
user_config.Upload_interval = Upload_interval
|
|
261
|
+
# Convert Upload_method to enum
|
|
262
|
+
if Upload_method.lower() == "lora":
|
|
263
|
+
user_config.Upload_method = Uploadmethod.LoRa
|
|
264
|
+
elif Upload_method.lower() == "wifi":
|
|
265
|
+
user_config.Upload_method = Uploadmethod.WiFi
|
|
266
|
+
else:
|
|
267
|
+
raise ValueError("Invalid Upload_method: must be 'LoRa' or 'WiFi'")
|
|
268
|
+
|
|
269
|
+
# Check for duplicates in Enabled_sensors
|
|
270
|
+
seen_sensors = set()
|
|
271
|
+
for sensor in Enabled_sensors:
|
|
272
|
+
if sensor in seen_sensors:
|
|
273
|
+
raise ValueError(f"Duplicate sensor found: {sensor}")
|
|
274
|
+
seen_sensors.add(sensor)
|
|
275
|
+
|
|
276
|
+
# Append to enabled_sensors based on the enum mapping
|
|
277
|
+
if sensor.lower() == "voltage":
|
|
278
|
+
user_config.enabled_sensors.append(EnabledSensor.Voltage)
|
|
279
|
+
elif sensor.lower() == "current":
|
|
280
|
+
user_config.enabled_sensors.append(EnabledSensor.Current)
|
|
281
|
+
elif sensor.lower() == "teros12":
|
|
282
|
+
user_config.enabled_sensors.append(EnabledSensor.Teros12)
|
|
283
|
+
elif sensor.lower() == "teros21":
|
|
284
|
+
user_config.enabled_sensors.append(EnabledSensor.Teros21)
|
|
285
|
+
elif sensor.lower() == "bme280":
|
|
286
|
+
user_config.enabled_sensors.append(EnabledSensor.BME280)
|
|
287
|
+
else:
|
|
288
|
+
raise ValueError(f"Invalid EnabledSensor: {sensor}")
|
|
289
|
+
user_config.Voltage_Slope = Voltage_Slope
|
|
290
|
+
user_config.Voltage_Offset = Voltage_Offset
|
|
291
|
+
user_config.Current_Slope = Current_Slope
|
|
292
|
+
user_config.Current_Offset = Current_Offset
|
|
293
|
+
user_config.WiFi_SSID = WiFi_SSID
|
|
294
|
+
user_config.WiFi_Password = WiFi_Password
|
|
295
|
+
user_config.API_Endpoint_URL = API_Endpoint_URL
|
|
296
|
+
user_config.API_Endpoint_Port = API_Endpoint_Port
|
|
297
|
+
|
|
298
|
+
return user_config.SerializeToString()
|
ents/proto/esp32.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from google.protobuf import message
|
|
2
|
+
from google.protobuf.json_format import MessageToDict
|
|
3
|
+
|
|
4
|
+
from .soil_power_sensor_pb2 import Esp32Command, PageCommand, TestCommand, WiFiCommand
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def encode_esp32command(cmd_type: str, **kwargs) -> bytes:
|
|
8
|
+
"""Encodes a command for the esp32
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
cmd: Type of command to encode
|
|
12
|
+
**kwargs: See other encode functions
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Bytes of protobuf serialized message
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
cmd = Esp32Command()
|
|
19
|
+
|
|
20
|
+
if cmd_type == "page":
|
|
21
|
+
page_cmd = encode_page_command(**kwargs)
|
|
22
|
+
cmd.page_command.CopyFrom(page_cmd)
|
|
23
|
+
elif cmd_type == "test":
|
|
24
|
+
test_cmd = encode_test_command(**kwargs)
|
|
25
|
+
cmd.test_command.CopyFrom(test_cmd)
|
|
26
|
+
elif cmd_type == "wifi":
|
|
27
|
+
wifi_cmd = encode_wifi_command(**kwargs)
|
|
28
|
+
cmd.wifi_command.CopyFrom(wifi_cmd)
|
|
29
|
+
else:
|
|
30
|
+
raise NotImplementedError(f"Command type {cmd_type} not implemented!")
|
|
31
|
+
|
|
32
|
+
return cmd.SerializeToString()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def encode_page_command(req: str, fd: int, bs: int = 0, n: int = 0) -> message:
|
|
36
|
+
"""Encodes a command for memory paging
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
req: Page request type
|
|
40
|
+
fd: File descriptor
|
|
41
|
+
bs: Block size
|
|
42
|
+
n: Number of bytes
|
|
43
|
+
|
|
44
|
+
Return:
|
|
45
|
+
PageCommand message
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
NotImplementedError: When page request type does not exist
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# look up table to request types
|
|
52
|
+
req_lut = {
|
|
53
|
+
"open": PageCommand.RequestType.OPEN,
|
|
54
|
+
"close": PageCommand.RequestType.CLOSE,
|
|
55
|
+
"read": PageCommand.RequestType.READ,
|
|
56
|
+
"write": PageCommand.RequestType.WRITE,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# ensure lower case
|
|
60
|
+
req = req.lower()
|
|
61
|
+
|
|
62
|
+
page_cmd = PageCommand()
|
|
63
|
+
try:
|
|
64
|
+
page_cmd.file_request = req_lut[req]
|
|
65
|
+
except KeyError as exc:
|
|
66
|
+
raise NotImplementedError(f"File request type {req} not implemented") from exc
|
|
67
|
+
page_cmd.file_descriptor = fd
|
|
68
|
+
page_cmd.block_size = bs
|
|
69
|
+
page_cmd.num_bytes = n
|
|
70
|
+
|
|
71
|
+
return page_cmd
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def encode_test_command(state: str, data: int) -> message:
|
|
75
|
+
"""Encodes a command for testing modules library
|
|
76
|
+
|
|
77
|
+
The following is of valid strings for state:
|
|
78
|
+
"receive": Data is received by the module
|
|
79
|
+
"receive_request": Data is received by the module indicating data for
|
|
80
|
+
subsequent request
|
|
81
|
+
"request": Data is sent my the module
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
state (str): State command string
|
|
85
|
+
data (int): Integer to test passing of data
|
|
86
|
+
|
|
87
|
+
Return:
|
|
88
|
+
TestCommand message
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
NotImplementedError: When the ChangeState type does not exist
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# look up table for state strings
|
|
95
|
+
cs_lut = {
|
|
96
|
+
"receive": TestCommand.ChangeState.RECEIVE,
|
|
97
|
+
"receive_request": TestCommand.ChangeState.RECEIVE_REQUEST,
|
|
98
|
+
"request": TestCommand.ChangeState.REQUEST,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ensure all lower case
|
|
102
|
+
state = state.lower()
|
|
103
|
+
|
|
104
|
+
test_cmd = TestCommand()
|
|
105
|
+
try:
|
|
106
|
+
test_cmd.state = cs_lut[state]
|
|
107
|
+
except KeyError as exc:
|
|
108
|
+
raise NotImplementedError(f"State command {state} not implemented") from exc
|
|
109
|
+
test_cmd.data = data
|
|
110
|
+
|
|
111
|
+
return test_cmd
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def encode_wifi_command(
|
|
115
|
+
_type: str,
|
|
116
|
+
ssid: str = "",
|
|
117
|
+
passwd: str = "",
|
|
118
|
+
url: str = "",
|
|
119
|
+
port: int = 0,
|
|
120
|
+
rc: int = 0,
|
|
121
|
+
ts: int = 0,
|
|
122
|
+
resp: bytes = b"",
|
|
123
|
+
) -> message:
|
|
124
|
+
"""Encodes a WiFi command
|
|
125
|
+
|
|
126
|
+
Valid strings for _type are:
|
|
127
|
+
"connect": Connecting to a WiFi network and any other setup
|
|
128
|
+
"post": Posting data to the endpoint
|
|
129
|
+
|
|
130
|
+
Not all arguments are necessary for valid commands.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
_type; Type of request
|
|
134
|
+
ssid: WiFi SSID
|
|
135
|
+
passwd: WiFi password
|
|
136
|
+
url: Endpoint URL
|
|
137
|
+
port: Port of webserver
|
|
138
|
+
rc: Return code
|
|
139
|
+
ts: Timestamp
|
|
140
|
+
resp: Binary data response from server
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
WiFiCommand message
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
NotImplementedError: When _type does not exist
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
# look up table for _type strings
|
|
150
|
+
_type_lut = {"connect": WiFiCommand.Type.CONNECT, "post": WiFiCommand.Type.POST}
|
|
151
|
+
|
|
152
|
+
# ensure all lower case
|
|
153
|
+
_type = _type.lower()
|
|
154
|
+
|
|
155
|
+
wifi_cmd = WiFiCommand()
|
|
156
|
+
try:
|
|
157
|
+
wifi_cmd.type = _type_lut[_type]
|
|
158
|
+
except KeyError as exc:
|
|
159
|
+
raise NotImplementedError(f"Type {_type} is not implemented") from exc
|
|
160
|
+
|
|
161
|
+
wifi_cmd.ssid = ssid
|
|
162
|
+
wifi_cmd.passwd = passwd
|
|
163
|
+
wifi_cmd.url = url
|
|
164
|
+
wifi_cmd.port = port
|
|
165
|
+
wifi_cmd.rc = rc
|
|
166
|
+
wifi_cmd.ts = ts
|
|
167
|
+
wifi_cmd.resp = resp
|
|
168
|
+
|
|
169
|
+
return wifi_cmd
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def decode_esp32command(data: bytes) -> dict:
|
|
173
|
+
cmd = Esp32Command()
|
|
174
|
+
|
|
175
|
+
# parse serialized message
|
|
176
|
+
cmd.ParseFromString(data)
|
|
177
|
+
|
|
178
|
+
# return dict
|
|
179
|
+
return MessageToDict(cmd, always_print_fields_with_no_presence=True)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: soil_power_sensor.proto
|
|
5
|
+
# Protobuf Python Version: 6.30.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
30,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'soil_power_sensor.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17soil_power_sensor.proto\"E\n\x13MeasurementMetadata\x12\x0f\n\x07\x63\x65ll_id\x18\x01 \x01(\r\x12\x11\n\tlogger_id\x18\x02 \x01(\r\x12\n\n\x02ts\x18\x03 \x01(\r\"4\n\x10PowerMeasurement\x12\x0f\n\x07voltage\x18\x02 \x01(\x01\x12\x0f\n\x07\x63urrent\x18\x03 \x01(\x01\"P\n\x12Teros12Measurement\x12\x0f\n\x07vwc_raw\x18\x02 \x01(\x01\x12\x0f\n\x07vwc_adj\x18\x03 \x01(\x01\x12\x0c\n\x04temp\x18\x04 \x01(\x01\x12\n\n\x02\x65\x63\x18\x05 \x01(\r\"6\n\x12Teros21Measurement\x12\x12\n\nmatric_pot\x18\x01 \x01(\x01\x12\x0c\n\x04temp\x18\x02 \x01(\x01\"<\n\x13Phytos31Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x14\n\x0cleaf_wetness\x18\x02 \x01(\x01\"L\n\x11\x42ME280Measurement\x12\x10\n\x08pressure\x18\x01 \x01(\r\x12\x13\n\x0btemperature\x18\x02 \x01(\x05\x12\x10\n\x08humidity\x18\x03 \x01(\r\"\x84\x02\n\x0bMeasurement\x12\"\n\x04meta\x18\x01 \x01(\x0b\x32\x14.MeasurementMetadata\x12\"\n\x05power\x18\x02 \x01(\x0b\x32\x11.PowerMeasurementH\x00\x12&\n\x07teros12\x18\x03 \x01(\x0b\x32\x13.Teros12MeasurementH\x00\x12(\n\x08phytos31\x18\x04 \x01(\x0b\x32\x14.Phytos31MeasurementH\x00\x12$\n\x06\x62me280\x18\x05 \x01(\x0b\x32\x12.BME280MeasurementH\x00\x12&\n\x07teros21\x18\x06 \x01(\x0b\x32\x13.Teros21MeasurementH\x00\x42\r\n\x0bmeasurement\"X\n\x08Response\x12$\n\x04resp\x18\x01 \x01(\x0e\x32\x16.Response.ResponseType\"&\n\x0cResponseType\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\"\x8b\x01\n\x0c\x45sp32Command\x12$\n\x0cpage_command\x18\x01 \x01(\x0b\x32\x0c.PageCommandH\x00\x12$\n\x0ctest_command\x18\x02 \x01(\x0b\x32\x0c.TestCommandH\x00\x12$\n\x0cwifi_command\x18\x03 \x01(\x0b\x32\x0c.WiFiCommandH\x00\x42\t\n\x07\x63ommand\"\xb6\x01\n\x0bPageCommand\x12.\n\x0c\x66ile_request\x18\x01 \x01(\x0e\x32\x18.PageCommand.RequestType\x12\x17\n\x0f\x66ile_descriptor\x18\x02 \x01(\r\x12\x12\n\nblock_size\x18\x03 \x01(\r\x12\x11\n\tnum_bytes\x18\x04 \x01(\r\"7\n\x0bRequestType\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04READ\x10\x02\x12\t\n\x05WRITE\x10\x03\"\x82\x01\n\x0bTestCommand\x12\'\n\x05state\x18\x01 \x01(\x0e\x32\x18.TestCommand.ChangeState\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x05\"<\n\x0b\x43hangeState\x12\x0b\n\x07RECEIVE\x10\x00\x12\x13\n\x0fRECEIVE_REQUEST\x10\x01\x12\x0b\n\x07REQUEST\x10\x02\"\xc1\x01\n\x0bWiFiCommand\x12\x1f\n\x04type\x18\x01 \x01(\x0e\x32\x11.WiFiCommand.Type\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0e\n\x06passwd\x18\x03 \x01(\t\x12\x0b\n\x03url\x18\x04 \x01(\t\x12\x0c\n\x04port\x18\x08 \x01(\r\x12\n\n\x02rc\x18\x05 \x01(\r\x12\n\n\x02ts\x18\x06 \x01(\r\x12\x0c\n\x04resp\x18\x07 \x01(\x0c\"2\n\x04Type\x12\x0b\n\x07\x43ONNECT\x10\x00\x12\x08\n\x04POST\x10\x01\x12\t\n\x05\x43HECK\x10\x02\x12\x08\n\x04TIME\x10\x03\"\xdc\x02\n\x11UserConfiguration\x12\x11\n\tlogger_id\x18\x01 \x01(\r\x12\x0f\n\x07\x63\x65ll_id\x18\x02 \x01(\r\x12$\n\rUpload_method\x18\x03 \x01(\x0e\x32\r.Uploadmethod\x12\x17\n\x0fUpload_interval\x18\x04 \x01(\r\x12\'\n\x0f\x65nabled_sensors\x18\x05 \x03(\x0e\x32\x0e.EnabledSensor\x12\x15\n\rVoltage_Slope\x18\x06 \x01(\x01\x12\x16\n\x0eVoltage_Offset\x18\x07 \x01(\x01\x12\x15\n\rCurrent_Slope\x18\x08 \x01(\x01\x12\x16\n\x0e\x43urrent_Offset\x18\t \x01(\x01\x12\x11\n\tWiFi_SSID\x18\n \x01(\t\x12\x15\n\rWiFi_Password\x18\x0b \x01(\t\x12\x18\n\x10\x41PI_Endpoint_URL\x18\x0c \x01(\t\x12\x19\n\x11\x41PI_Endpoint_Port\x18\r \x01(\r*O\n\rEnabledSensor\x12\x0b\n\x07Voltage\x10\x00\x12\x0b\n\x07\x43urrent\x10\x01\x12\x0b\n\x07Teros12\x10\x02\x12\x0b\n\x07Teros21\x10\x03\x12\n\n\x06\x42ME280\x10\x04*\"\n\x0cUploadmethod\x12\x08\n\x04LoRa\x10\x00\x12\x08\n\x04WiFi\x10\x01\x62\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'soil_power_sensor_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
DESCRIPTOR._loaded_options = None
|
|
34
|
+
_globals['_ENABLEDSENSOR']._serialized_start=1790
|
|
35
|
+
_globals['_ENABLEDSENSOR']._serialized_end=1869
|
|
36
|
+
_globals['_UPLOADMETHOD']._serialized_start=1871
|
|
37
|
+
_globals['_UPLOADMETHOD']._serialized_end=1905
|
|
38
|
+
_globals['_MEASUREMENTMETADATA']._serialized_start=27
|
|
39
|
+
_globals['_MEASUREMENTMETADATA']._serialized_end=96
|
|
40
|
+
_globals['_POWERMEASUREMENT']._serialized_start=98
|
|
41
|
+
_globals['_POWERMEASUREMENT']._serialized_end=150
|
|
42
|
+
_globals['_TEROS12MEASUREMENT']._serialized_start=152
|
|
43
|
+
_globals['_TEROS12MEASUREMENT']._serialized_end=232
|
|
44
|
+
_globals['_TEROS21MEASUREMENT']._serialized_start=234
|
|
45
|
+
_globals['_TEROS21MEASUREMENT']._serialized_end=288
|
|
46
|
+
_globals['_PHYTOS31MEASUREMENT']._serialized_start=290
|
|
47
|
+
_globals['_PHYTOS31MEASUREMENT']._serialized_end=350
|
|
48
|
+
_globals['_BME280MEASUREMENT']._serialized_start=352
|
|
49
|
+
_globals['_BME280MEASUREMENT']._serialized_end=428
|
|
50
|
+
_globals['_MEASUREMENT']._serialized_start=431
|
|
51
|
+
_globals['_MEASUREMENT']._serialized_end=691
|
|
52
|
+
_globals['_RESPONSE']._serialized_start=693
|
|
53
|
+
_globals['_RESPONSE']._serialized_end=781
|
|
54
|
+
_globals['_RESPONSE_RESPONSETYPE']._serialized_start=743
|
|
55
|
+
_globals['_RESPONSE_RESPONSETYPE']._serialized_end=781
|
|
56
|
+
_globals['_ESP32COMMAND']._serialized_start=784
|
|
57
|
+
_globals['_ESP32COMMAND']._serialized_end=923
|
|
58
|
+
_globals['_PAGECOMMAND']._serialized_start=926
|
|
59
|
+
_globals['_PAGECOMMAND']._serialized_end=1108
|
|
60
|
+
_globals['_PAGECOMMAND_REQUESTTYPE']._serialized_start=1053
|
|
61
|
+
_globals['_PAGECOMMAND_REQUESTTYPE']._serialized_end=1108
|
|
62
|
+
_globals['_TESTCOMMAND']._serialized_start=1111
|
|
63
|
+
_globals['_TESTCOMMAND']._serialized_end=1241
|
|
64
|
+
_globals['_TESTCOMMAND_CHANGESTATE']._serialized_start=1181
|
|
65
|
+
_globals['_TESTCOMMAND_CHANGESTATE']._serialized_end=1241
|
|
66
|
+
_globals['_WIFICOMMAND']._serialized_start=1244
|
|
67
|
+
_globals['_WIFICOMMAND']._serialized_end=1437
|
|
68
|
+
_globals['_WIFICOMMAND_TYPE']._serialized_start=1387
|
|
69
|
+
_globals['_WIFICOMMAND_TYPE']._serialized_end=1437
|
|
70
|
+
_globals['_USERCONFIGURATION']._serialized_start=1440
|
|
71
|
+
_globals['_USERCONFIGURATION']._serialized_end=1788
|
|
72
|
+
# @@protoc_insertion_point(module_scope)
|
|
File without changes
|