layrz-sdk 3.1.12__py3-none-any.whl → 3.1.13__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.
Potentially problematic release.
This version of layrz-sdk might be problematic. Click here for more details.
- layrz_sdk/__init__.py +1 -1
- layrz_sdk/constants.py +5 -5
- layrz_sdk/entities/__init__.py +129 -129
- layrz_sdk/entities/asset.py +60 -60
- layrz_sdk/entities/asset_operation_mode.py +31 -31
- layrz_sdk/entities/broadcast_request.py +12 -12
- layrz_sdk/entities/broadcast_response.py +12 -12
- layrz_sdk/entities/broadcast_result.py +20 -20
- layrz_sdk/entities/broadcast_status.py +28 -28
- layrz_sdk/entities/case.py +48 -48
- layrz_sdk/entities/case_ignored_status.py +26 -26
- layrz_sdk/entities/case_status.py +23 -23
- layrz_sdk/entities/charts/axis_config.py +15 -15
- layrz_sdk/entities/charts/bar_chart.py +175 -175
- layrz_sdk/entities/charts/chart_alignment.py +27 -27
- layrz_sdk/entities/charts/chart_color.py +44 -44
- layrz_sdk/entities/charts/chart_configuration.py +10 -10
- layrz_sdk/entities/charts/chart_data_serie.py +19 -19
- layrz_sdk/entities/charts/chart_data_serie_type.py +28 -28
- layrz_sdk/entities/charts/chart_data_type.py +27 -27
- layrz_sdk/entities/charts/chart_render_technology.py +30 -30
- layrz_sdk/entities/charts/column_chart.py +201 -201
- layrz_sdk/entities/charts/html_chart.py +38 -38
- layrz_sdk/entities/charts/line_chart.py +248 -248
- layrz_sdk/entities/charts/map_center_type.py +22 -22
- layrz_sdk/entities/charts/map_chart.py +108 -108
- layrz_sdk/entities/charts/map_point.py +22 -22
- layrz_sdk/entities/charts/number_chart.py +54 -54
- layrz_sdk/entities/charts/pie_chart.py +131 -131
- layrz_sdk/entities/charts/radar_chart.py +81 -81
- layrz_sdk/entities/charts/radial_bar_chart.py +131 -131
- layrz_sdk/entities/charts/scatter_chart.py +210 -210
- layrz_sdk/entities/charts/scatter_serie.py +13 -13
- layrz_sdk/entities/charts/scatter_serie_item.py +8 -8
- layrz_sdk/entities/charts/table_chart.py +54 -54
- layrz_sdk/entities/charts/table_header.py +8 -8
- layrz_sdk/entities/charts/table_row.py +9 -9
- layrz_sdk/entities/charts/timeline_chart.py +79 -79
- layrz_sdk/entities/charts/timeline_serie.py +10 -10
- layrz_sdk/entities/charts/timeline_serie_item.py +12 -12
- layrz_sdk/entities/checkpoint.py +17 -17
- layrz_sdk/entities/comment.py +16 -16
- layrz_sdk/entities/custom_field.py +10 -10
- layrz_sdk/entities/custom_report_page.py +40 -16
- layrz_sdk/entities/device.py +13 -13
- layrz_sdk/entities/event.py +23 -23
- layrz_sdk/entities/geofence.py +11 -11
- layrz_sdk/entities/last_message.py +12 -12
- layrz_sdk/entities/message.py +23 -23
- layrz_sdk/entities/outbound_service.py +10 -10
- layrz_sdk/entities/position.py +116 -116
- layrz_sdk/entities/presence_type.py +16 -16
- layrz_sdk/entities/report.py +289 -283
- layrz_sdk/entities/report_col.py +40 -40
- layrz_sdk/entities/report_configuration.py +8 -8
- layrz_sdk/entities/report_data_type.py +28 -28
- layrz_sdk/entities/report_format.py +27 -27
- layrz_sdk/entities/report_header.py +43 -43
- layrz_sdk/entities/report_page.py +15 -15
- layrz_sdk/entities/report_row.py +28 -28
- layrz_sdk/entities/sensor.py +11 -11
- layrz_sdk/entities/text_alignment.py +26 -26
- layrz_sdk/entities/trigger.py +11 -11
- layrz_sdk/entities/user.py +10 -10
- layrz_sdk/entities/waypoint.py +18 -18
- layrz_sdk/helpers/__init__.py +5 -5
- layrz_sdk/helpers/color.py +44 -44
- layrz_sdk/lcl/__init__.py +5 -5
- layrz_sdk/lcl/core.py +848 -848
- {layrz_sdk-3.1.12.dist-info → layrz_sdk-3.1.13.dist-info}/METADATA +49 -49
- layrz_sdk-3.1.13.dist-info/RECORD +75 -0
- {layrz_sdk-3.1.12.dist-info → layrz_sdk-3.1.13.dist-info}/WHEEL +1 -1
- {layrz_sdk-3.1.12.dist-info → layrz_sdk-3.1.13.dist-info}/licenses/LICENSE +6 -6
- layrz_sdk-3.1.12.dist-info/RECORD +0 -75
- {layrz_sdk-3.1.12.dist-info → layrz_sdk-3.1.13.dist-info}/top_level.txt +0 -0
layrz_sdk/lcl/core.py
CHANGED
|
@@ -1,848 +1,848 @@
|
|
|
1
|
-
"""Layrz Compute Language SDK"""
|
|
2
|
-
|
|
3
|
-
# ruff: noqa: ANN401
|
|
4
|
-
from typing import Any, Optional
|
|
5
|
-
|
|
6
|
-
from layrz_sdk.entities.message import PayloadType
|
|
7
|
-
|
|
8
|
-
PATTERN_INVALID = 'Pattern should be string, received {received}'
|
|
9
|
-
INVALID_NUMBER_OF_PARAMS = 'Invalid number of arguments - Expected {expected} - Given {received}'
|
|
10
|
-
DIFFERENT_TYPES_RANGES = 'Invalid data range, value: {arg1} - Minimum: {arg2} - Maximum: {arg3}'
|
|
11
|
-
DIFFERENT_TYPES = 'Invalid data types - arg1: {arg1} - arg2: {arg2}'
|
|
12
|
-
INVALID_ARGUMENTS = 'Invalid arguments - {e}'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class LclCore:
|
|
16
|
-
"""Layrz Compute Language SDK"""
|
|
17
|
-
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
script: str = '',
|
|
21
|
-
sensors: Optional[PayloadType] = None,
|
|
22
|
-
previous_sensors: Optional[PayloadType] = None,
|
|
23
|
-
payload: Optional[PayloadType] = None,
|
|
24
|
-
asset_constants: Optional[dict[str, Any]] = None,
|
|
25
|
-
custom_fields: Optional[dict[str, Any]] = None,
|
|
26
|
-
) -> None:
|
|
27
|
-
"""
|
|
28
|
-
Creates a new instance of LclCore
|
|
29
|
-
|
|
30
|
-
:param script: Script to be executed
|
|
31
|
-
:type script: str
|
|
32
|
-
:param sensors: Sensors dictionary
|
|
33
|
-
:type sensors: Optional[PayloadType]
|
|
34
|
-
:param previous_sensors: Previous sensors dictionary
|
|
35
|
-
:type previous_sensors: Optional[PayloadType]
|
|
36
|
-
:param payload: Payload dictionary
|
|
37
|
-
:type payload: Optional[PayloadType]
|
|
38
|
-
:param asset_constants: Asset constants dictionary
|
|
39
|
-
:type asset_constants: Optional[dict[str, Any]]
|
|
40
|
-
:param custom_fields: Custom fields dictionary
|
|
41
|
-
:type custom_fields: Optional[dict[str, Any]]
|
|
42
|
-
|
|
43
|
-
:return: None
|
|
44
|
-
:rtype: None
|
|
45
|
-
"""
|
|
46
|
-
if sensors is None:
|
|
47
|
-
sensors = {}
|
|
48
|
-
|
|
49
|
-
self._sensors = sensors
|
|
50
|
-
if previous_sensors is None:
|
|
51
|
-
previous_sensors = {}
|
|
52
|
-
self._previous_sensors = previous_sensors
|
|
53
|
-
if payload is None:
|
|
54
|
-
payload = {}
|
|
55
|
-
self._payload = payload
|
|
56
|
-
|
|
57
|
-
if asset_constants is None:
|
|
58
|
-
asset_constants = {}
|
|
59
|
-
self._asset_constants = asset_constants
|
|
60
|
-
|
|
61
|
-
if custom_fields is None:
|
|
62
|
-
custom_fields = {}
|
|
63
|
-
self._custom_fields = custom_fields
|
|
64
|
-
|
|
65
|
-
self._script = script
|
|
66
|
-
|
|
67
|
-
def perform(
|
|
68
|
-
self,
|
|
69
|
-
additional_globals: Optional[dict[str, Any]] = None,
|
|
70
|
-
additional_locals: Optional[dict[str, Any]] = None,
|
|
71
|
-
) -> str:
|
|
72
|
-
"""
|
|
73
|
-
Perform script using Layrz Compute Language
|
|
74
|
-
|
|
75
|
-
:param additional_globals: Additional global variables
|
|
76
|
-
:type additional_globals: Optional[dict[str, Any]]
|
|
77
|
-
:param additional_locals: Additional local variables
|
|
78
|
-
:type additional_locals: Optional[dict[str, Any]]
|
|
79
|
-
|
|
80
|
-
:return: Result of the script in JSON format
|
|
81
|
-
:rtype: str
|
|
82
|
-
"""
|
|
83
|
-
try:
|
|
84
|
-
local_variables = {
|
|
85
|
-
'payload': self._payload,
|
|
86
|
-
'sensors': self._sensors,
|
|
87
|
-
'custom_fields': self._custom_fields,
|
|
88
|
-
'previous_sensors': self._previous_sensors,
|
|
89
|
-
'asset_constants': self._asset_constants,
|
|
90
|
-
}
|
|
91
|
-
if additional_locals:
|
|
92
|
-
local_variables.update(additional_locals)
|
|
93
|
-
|
|
94
|
-
## Define global variables and functions
|
|
95
|
-
global_functions = {
|
|
96
|
-
'GET_PARAM': self.GET_PARAM,
|
|
97
|
-
'GET_SENSOR': self.GET_SENSOR,
|
|
98
|
-
'CONSTANT': self.CONSTANT,
|
|
99
|
-
'GET_CUSTOM_FIELD': self.GET_CUSTOM_FIELD,
|
|
100
|
-
'COMPARE': self.COMPARE,
|
|
101
|
-
'OR_OPERATOR': self.OR_OPERATOR,
|
|
102
|
-
'AND_OPERATOR': self.AND_OPERATOR,
|
|
103
|
-
'SUM': self.SUM,
|
|
104
|
-
'SUBSTRACT': self.SUBSTRACT,
|
|
105
|
-
'MULTIPLY': self.MULTIPLY,
|
|
106
|
-
'DIVIDE': self.DIVIDE,
|
|
107
|
-
'TO_BOOL': self.TO_BOOL,
|
|
108
|
-
'TO_STR': self.TO_STR,
|
|
109
|
-
'TO_INT': self.TO_INT,
|
|
110
|
-
'CEIL': self.CEIL,
|
|
111
|
-
'FLOOR': self.FLOOR,
|
|
112
|
-
'ROUND': self.ROUND,
|
|
113
|
-
'SQRT': self.SQRT,
|
|
114
|
-
'CONCAT': self.CONCAT,
|
|
115
|
-
'NOW': self.NOW,
|
|
116
|
-
'RANDOM': self.RANDOM,
|
|
117
|
-
'RANDOM_INT': self.RANDOM_INT,
|
|
118
|
-
'GREATER_THAN_OR_EQUALS_TO': self.GREATER_THAN_OR_EQUALS_TO,
|
|
119
|
-
'GREATER_THAN': self.GREATER_THAN,
|
|
120
|
-
'LESS_THAN_OR_EQUALS_TO': self.LESS_THAN_OR_EQUALS_TO,
|
|
121
|
-
'LESS_THAN': self.LESS_THAN,
|
|
122
|
-
'DIFFERENT': self.DIFFERENT,
|
|
123
|
-
'HEX_TO_STR': self.HEX_TO_STR,
|
|
124
|
-
'STR_TO_HEX': self.STR_TO_HEX,
|
|
125
|
-
'HEX_TO_INT': self.HEX_TO_INT,
|
|
126
|
-
'INT_TO_HEX': self.INT_TO_HEX,
|
|
127
|
-
'TO_FLOAT': self.TO_FLOAT,
|
|
128
|
-
'GET_DISTANCE_TRAVELED': self.GET_DISTANCE_TRAVELED,
|
|
129
|
-
'GET_PREVIOUS_SENSOR': self.GET_PREVIOUS_SENSOR,
|
|
130
|
-
'IS_PARAMETER_PRESENT': self.IS_PARAMETER_PRESENT,
|
|
131
|
-
'IS_SENSOR_PRESENT': self.IS_SENSOR_PRESENT,
|
|
132
|
-
'INSIDE_RANGE': self.INSIDE_RANGE,
|
|
133
|
-
'OUTSIDE_RANGE': self.OUTSIDE_RANGE,
|
|
134
|
-
'GET_TIME_DIFFERENCE': self.GET_TIME_DIFFERENCE,
|
|
135
|
-
'IF': self.IF,
|
|
136
|
-
'REGEX': self.REGEX,
|
|
137
|
-
'IS_NONE': self.IS_NONE,
|
|
138
|
-
'NOT': self.NOT,
|
|
139
|
-
'CONTAINS': self.CONTAINS,
|
|
140
|
-
'STARTS_WITH': self.STARTS_WITH,
|
|
141
|
-
'ENDS_WITH': self.ENDS_WITH,
|
|
142
|
-
'PRIMARY_DEVICE': self.PRIMARY_DEVICE,
|
|
143
|
-
'SUBSTRING': self.SUBSTRING,
|
|
144
|
-
'UNIX_TO_STR': self.UNIX_TO_STR,
|
|
145
|
-
'VERSION': self.VERSION,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if additional_globals:
|
|
149
|
-
global_functions.update(additional_globals)
|
|
150
|
-
|
|
151
|
-
import json
|
|
152
|
-
|
|
153
|
-
result = json.dumps(eval(self._script, global_functions, local_variables))
|
|
154
|
-
|
|
155
|
-
return result
|
|
156
|
-
except Exception as err:
|
|
157
|
-
import json
|
|
158
|
-
|
|
159
|
-
return json.dumps(INVALID_ARGUMENTS.format(e=err))
|
|
160
|
-
|
|
161
|
-
def _standarize_datatypes(self, args: list[Any]) -> list[Any]:
|
|
162
|
-
"""Standarize data types"""
|
|
163
|
-
result_args = []
|
|
164
|
-
|
|
165
|
-
for arg in args:
|
|
166
|
-
if isinstance(arg, (float, int)):
|
|
167
|
-
result_args.append(float(arg))
|
|
168
|
-
else:
|
|
169
|
-
result_args.append(arg)
|
|
170
|
-
|
|
171
|
-
return result_args
|
|
172
|
-
|
|
173
|
-
def GET_PARAM(self, *args: list[Any]) -> Any:
|
|
174
|
-
"""GET_PARAM Function"""
|
|
175
|
-
if len(args) > 2:
|
|
176
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
177
|
-
|
|
178
|
-
if args[0] is None:
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
if len(args) > 1:
|
|
182
|
-
return self._payload.get(args[0], args[1]) # type: ignore
|
|
183
|
-
return self._payload.get(args[0], None) # type: ignore
|
|
184
|
-
|
|
185
|
-
def GET_DISTANCE_TRAVELED(self, *args: list[Any]) -> str | float:
|
|
186
|
-
"""GET_DISTANCE_TRAVELED Function"""
|
|
187
|
-
if len(args) > 0:
|
|
188
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
189
|
-
return self._asset_constants.get('distanceTraveled', 0) # type: ignore
|
|
190
|
-
|
|
191
|
-
def GET_PREVIOUS_SENSOR(self, *args: list[Any]) -> Any:
|
|
192
|
-
"""GET_PREVIOUS_SENSOR Function"""
|
|
193
|
-
if len(args) < 1:
|
|
194
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
195
|
-
|
|
196
|
-
if len(args) > 2:
|
|
197
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
198
|
-
|
|
199
|
-
if args[0] is None:
|
|
200
|
-
return None
|
|
201
|
-
|
|
202
|
-
if len(args) > 1:
|
|
203
|
-
return self._previous_sensors.get(args[0], args[1]) # type: ignore
|
|
204
|
-
return self._previous_sensors.get(args[0], None) # type: ignore
|
|
205
|
-
|
|
206
|
-
def GET_SENSOR(self, *args: list[Any]) -> Any:
|
|
207
|
-
"""GET_SENSOR Function"""
|
|
208
|
-
if len(args) < 1:
|
|
209
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
210
|
-
|
|
211
|
-
if len(args) > 2:
|
|
212
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
213
|
-
|
|
214
|
-
if args[0] is None:
|
|
215
|
-
return None
|
|
216
|
-
|
|
217
|
-
if len(args) > 1:
|
|
218
|
-
return self._sensors.get(args[0], args[1]) # type: ignore
|
|
219
|
-
return self._sensors.get(args[0], None) # type: ignore
|
|
220
|
-
|
|
221
|
-
def CONSTANT(self, *args: list[Any]) -> Any:
|
|
222
|
-
"""CONSTANT Function"""
|
|
223
|
-
if len(args) > 1:
|
|
224
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
225
|
-
return args[0]
|
|
226
|
-
|
|
227
|
-
def GET_CUSTOM_FIELD(self, *args: list[Any]) -> str:
|
|
228
|
-
"""GET_CUSTOM_FIELD Function"""
|
|
229
|
-
if len(args) > 2:
|
|
230
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
231
|
-
|
|
232
|
-
if args[0] is None:
|
|
233
|
-
return None
|
|
234
|
-
|
|
235
|
-
if len(args) > 1:
|
|
236
|
-
return self._custom_fields.get(args[0], args[1]) # type: ignore
|
|
237
|
-
|
|
238
|
-
return self._custom_fields.get(args[0], '') # type: ignore
|
|
239
|
-
|
|
240
|
-
def COMPARE(self, *args: list[Any]) -> str | None | bool:
|
|
241
|
-
"""COMPARE Function"""
|
|
242
|
-
if len(args) != 2:
|
|
243
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
244
|
-
|
|
245
|
-
if args[0] is None or args[1] is None:
|
|
246
|
-
return None
|
|
247
|
-
|
|
248
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
249
|
-
|
|
250
|
-
if not isinstance(args[0], type(args[1])):
|
|
251
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
252
|
-
return args[0] == args[1]
|
|
253
|
-
|
|
254
|
-
def OR_OPERATOR(self, *args: list[Any]) -> bool:
|
|
255
|
-
"""OR_OPERATOR Function"""
|
|
256
|
-
result = False
|
|
257
|
-
|
|
258
|
-
for val in args:
|
|
259
|
-
if val is None:
|
|
260
|
-
return None
|
|
261
|
-
if isinstance(val, bool):
|
|
262
|
-
result = result or val
|
|
263
|
-
|
|
264
|
-
return result
|
|
265
|
-
|
|
266
|
-
def AND_OPERATOR(self, *args: list[Any]) -> bool:
|
|
267
|
-
"""AND_OPERATOR Function"""
|
|
268
|
-
result = False
|
|
269
|
-
is_first = True
|
|
270
|
-
|
|
271
|
-
for val in args:
|
|
272
|
-
if val is None:
|
|
273
|
-
return None
|
|
274
|
-
|
|
275
|
-
if is_first:
|
|
276
|
-
is_first = False
|
|
277
|
-
result = val # type: ignore
|
|
278
|
-
elif isinstance(val, bool):
|
|
279
|
-
result = result and val
|
|
280
|
-
|
|
281
|
-
return result
|
|
282
|
-
|
|
283
|
-
def SUM(self, *args: list[Any]) -> float:
|
|
284
|
-
"""SUM Function"""
|
|
285
|
-
result = 0
|
|
286
|
-
|
|
287
|
-
for num in args:
|
|
288
|
-
if num is None:
|
|
289
|
-
return None
|
|
290
|
-
|
|
291
|
-
try:
|
|
292
|
-
result += float(num) # type: ignore
|
|
293
|
-
except Exception:
|
|
294
|
-
pass
|
|
295
|
-
|
|
296
|
-
return result
|
|
297
|
-
|
|
298
|
-
def SUBSTRACT(self, *args: list[Any]) -> float:
|
|
299
|
-
"""SUBSTRACT Function"""
|
|
300
|
-
result = 0
|
|
301
|
-
is_first = True
|
|
302
|
-
|
|
303
|
-
for num in args:
|
|
304
|
-
if num is None:
|
|
305
|
-
return None
|
|
306
|
-
|
|
307
|
-
try:
|
|
308
|
-
if is_first:
|
|
309
|
-
result = float(num) # type: ignore
|
|
310
|
-
is_first = False
|
|
311
|
-
else:
|
|
312
|
-
result -= float(num) # type: ignore
|
|
313
|
-
except Exception:
|
|
314
|
-
pass
|
|
315
|
-
|
|
316
|
-
return result
|
|
317
|
-
|
|
318
|
-
def MULTIPLY(self, *args: list[Any]) -> float:
|
|
319
|
-
"""MULTIPLY Function"""
|
|
320
|
-
result = 0
|
|
321
|
-
is_first = True
|
|
322
|
-
|
|
323
|
-
for num in args:
|
|
324
|
-
if num is None:
|
|
325
|
-
return None
|
|
326
|
-
|
|
327
|
-
try:
|
|
328
|
-
if is_first:
|
|
329
|
-
is_first = False
|
|
330
|
-
result = float(num) # type: ignore
|
|
331
|
-
else:
|
|
332
|
-
result *= float(num) # type: ignore
|
|
333
|
-
except Exception:
|
|
334
|
-
pass
|
|
335
|
-
|
|
336
|
-
return result
|
|
337
|
-
|
|
338
|
-
def DIVIDE(self, *args: list[Any]) -> float:
|
|
339
|
-
"""DIVIDE Function"""
|
|
340
|
-
result = 0
|
|
341
|
-
is_first = True
|
|
342
|
-
|
|
343
|
-
for num in args:
|
|
344
|
-
if num is None:
|
|
345
|
-
return None
|
|
346
|
-
|
|
347
|
-
try:
|
|
348
|
-
if is_first:
|
|
349
|
-
is_first = False
|
|
350
|
-
result = float(num) # type: ignore
|
|
351
|
-
else:
|
|
352
|
-
result /= float(num) # type: ignore
|
|
353
|
-
except Exception:
|
|
354
|
-
pass
|
|
355
|
-
|
|
356
|
-
return result
|
|
357
|
-
|
|
358
|
-
def TO_BOOL(self, *args: list[Any]) -> str | None | bool:
|
|
359
|
-
"""TO_BOOL Function"""
|
|
360
|
-
if len(args) > 1:
|
|
361
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
362
|
-
|
|
363
|
-
if args[0] is None:
|
|
364
|
-
return None
|
|
365
|
-
|
|
366
|
-
return bool(args[0])
|
|
367
|
-
|
|
368
|
-
def TO_STR(self, *args: list[Any]) -> str | None:
|
|
369
|
-
"""TO_STR Function"""
|
|
370
|
-
if len(args) > 1:
|
|
371
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
372
|
-
|
|
373
|
-
if args[0] is None:
|
|
374
|
-
return None
|
|
375
|
-
|
|
376
|
-
return str(args[0])
|
|
377
|
-
|
|
378
|
-
def TO_INT(self, *args: list[Any]) -> str | None | int:
|
|
379
|
-
"""TO_INT Function"""
|
|
380
|
-
if len(args) > 1:
|
|
381
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
382
|
-
|
|
383
|
-
if args[0] is None:
|
|
384
|
-
return None
|
|
385
|
-
|
|
386
|
-
return int(args[0]) # type: ignore
|
|
387
|
-
|
|
388
|
-
def CEIL(self, *args: list[Any]) -> str | None | int:
|
|
389
|
-
"""CEIL Function"""
|
|
390
|
-
if len(args) > 1:
|
|
391
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
392
|
-
|
|
393
|
-
if args[0] is None:
|
|
394
|
-
return None
|
|
395
|
-
|
|
396
|
-
if not isinstance(args[0], (int, float)):
|
|
397
|
-
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
398
|
-
|
|
399
|
-
import math
|
|
400
|
-
|
|
401
|
-
return math.ceil(args[0])
|
|
402
|
-
|
|
403
|
-
def FLOOR(self, *args: list[Any]) -> str | None | int:
|
|
404
|
-
"""FLOOR Function"""
|
|
405
|
-
if len(args) > 1:
|
|
406
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
407
|
-
|
|
408
|
-
if args[0] is None:
|
|
409
|
-
return None
|
|
410
|
-
|
|
411
|
-
if not isinstance(args[0], (int, float)):
|
|
412
|
-
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
413
|
-
|
|
414
|
-
import math
|
|
415
|
-
|
|
416
|
-
return math.floor(args[0])
|
|
417
|
-
|
|
418
|
-
def ROUND(self, *args: list[Any]) -> str | None | int:
|
|
419
|
-
"""ROUND Function"""
|
|
420
|
-
if len(args) > 1:
|
|
421
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
422
|
-
|
|
423
|
-
if args[0] is None:
|
|
424
|
-
return None
|
|
425
|
-
|
|
426
|
-
if not isinstance(args[0], (int, float)):
|
|
427
|
-
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
428
|
-
|
|
429
|
-
return round(args[0])
|
|
430
|
-
|
|
431
|
-
def SQRT(self, *args: list[Any]) -> str | None | float:
|
|
432
|
-
"""SQRT Function"""
|
|
433
|
-
if len(args) > 1:
|
|
434
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
435
|
-
|
|
436
|
-
if args[0] is None:
|
|
437
|
-
return None
|
|
438
|
-
|
|
439
|
-
import math
|
|
440
|
-
|
|
441
|
-
return math.sqrt(args[0]) # type: ignore
|
|
442
|
-
|
|
443
|
-
def CONCAT(self, *args: list[Any]) -> str | None:
|
|
444
|
-
"""CONCAT Function"""
|
|
445
|
-
for val in args:
|
|
446
|
-
if val is None:
|
|
447
|
-
return None
|
|
448
|
-
|
|
449
|
-
return ''.join([str(val) for val in args])
|
|
450
|
-
|
|
451
|
-
def RANDOM(self, *args: list[Any]) -> float | str:
|
|
452
|
-
"""RANDOM Function"""
|
|
453
|
-
if len(args) > 2:
|
|
454
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
455
|
-
if len(args) < 2:
|
|
456
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
457
|
-
|
|
458
|
-
if args[0] is None or args[1] is None:
|
|
459
|
-
return None
|
|
460
|
-
|
|
461
|
-
import random
|
|
462
|
-
|
|
463
|
-
return random.random() * (float(args[1]) - float(args[0])) + float(args[0]) # type: ignore
|
|
464
|
-
|
|
465
|
-
def RANDOM_INT(self, *args: list[Any]) -> int | str:
|
|
466
|
-
"""RANDOM_INT Function"""
|
|
467
|
-
if len(args) != 2:
|
|
468
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
469
|
-
|
|
470
|
-
if args[0] is None or args[1] is None:
|
|
471
|
-
return None
|
|
472
|
-
|
|
473
|
-
import random
|
|
474
|
-
|
|
475
|
-
return random.randint(int(args[0]), int(args[1])) # type: ignore
|
|
476
|
-
|
|
477
|
-
def GREATER_THAN_OR_EQUALS_TO(self, *args: list[Any]) -> str | None | bool:
|
|
478
|
-
"""GREATER_THAN_OR_EQUALS_TO Function"""
|
|
479
|
-
if len(args) > 2:
|
|
480
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
481
|
-
if len(args) < 2:
|
|
482
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
483
|
-
|
|
484
|
-
if args[0] is None or args[1] is None:
|
|
485
|
-
return None
|
|
486
|
-
|
|
487
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
488
|
-
|
|
489
|
-
if not isinstance(args[0], type(args[1])):
|
|
490
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
491
|
-
return args[0] >= args[1]
|
|
492
|
-
|
|
493
|
-
def GREATER_THAN(self, *args: list[Any]) -> str | None | bool:
|
|
494
|
-
"""GREATER_THAN Function"""
|
|
495
|
-
if len(args) > 2:
|
|
496
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
497
|
-
if len(args) < 2:
|
|
498
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
499
|
-
|
|
500
|
-
if args[0] is None or args[1] is None:
|
|
501
|
-
return None
|
|
502
|
-
|
|
503
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
504
|
-
|
|
505
|
-
if not isinstance(args[0], type(args[1])):
|
|
506
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
507
|
-
return args[0] > args[1]
|
|
508
|
-
|
|
509
|
-
def LESS_THAN_OR_EQUALS_TO(self, *args: list[Any]) -> str | None | bool:
|
|
510
|
-
"""LESS_THAN_OR_EQUALS_TO Function"""
|
|
511
|
-
if len(args) > 2:
|
|
512
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
513
|
-
if len(args) < 2:
|
|
514
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
515
|
-
|
|
516
|
-
if args[0] is None or args[1] is None:
|
|
517
|
-
return None
|
|
518
|
-
|
|
519
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
520
|
-
|
|
521
|
-
if not isinstance(args[0], type(args[1])):
|
|
522
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
523
|
-
return args[0] <= args[1]
|
|
524
|
-
|
|
525
|
-
def LESS_THAN(self, *args: list[Any]) -> str | None | bool:
|
|
526
|
-
"""LESS_THAN Function"""
|
|
527
|
-
if len(args) > 2:
|
|
528
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
529
|
-
if len(args) < 2:
|
|
530
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
531
|
-
|
|
532
|
-
if args[0] is None or args[1] is None:
|
|
533
|
-
return None
|
|
534
|
-
|
|
535
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
536
|
-
|
|
537
|
-
if not isinstance(args[0], type(args[1])):
|
|
538
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
539
|
-
return args[0] < args[1]
|
|
540
|
-
|
|
541
|
-
def DIFFERENT(self, *args: list[Any]) -> str | None | bool:
|
|
542
|
-
"""DIFFERENT Function"""
|
|
543
|
-
if len(args) > 2:
|
|
544
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
545
|
-
if len(args) < 2:
|
|
546
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
547
|
-
|
|
548
|
-
if args[0] is None or args[1] is None:
|
|
549
|
-
return None
|
|
550
|
-
|
|
551
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
552
|
-
|
|
553
|
-
if not isinstance(args[0], type(args[1])):
|
|
554
|
-
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
555
|
-
return args[0] != args[1]
|
|
556
|
-
|
|
557
|
-
def HEX_TO_STR(self, *args: list[Any]) -> str | None:
|
|
558
|
-
"""HEX_TO_STR Function"""
|
|
559
|
-
if len(args) > 1:
|
|
560
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
561
|
-
|
|
562
|
-
if args[0] is None:
|
|
563
|
-
return None
|
|
564
|
-
|
|
565
|
-
hexa = args[0]
|
|
566
|
-
if hexa.startswith('0x'): # type: ignore
|
|
567
|
-
hexa = hexa[2:]
|
|
568
|
-
|
|
569
|
-
try:
|
|
570
|
-
byte_array = bytes.fromhex(hexa) # type: ignore
|
|
571
|
-
return byte_array.decode('ASCII')
|
|
572
|
-
except Exception:
|
|
573
|
-
return 'Invalid hex string'
|
|
574
|
-
|
|
575
|
-
def STR_TO_HEX(self, *args: list[Any]) -> str | None:
|
|
576
|
-
"""STR_TO_HEX Function"""
|
|
577
|
-
if len(args) > 1:
|
|
578
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
579
|
-
|
|
580
|
-
if args[0] is None:
|
|
581
|
-
return None
|
|
582
|
-
|
|
583
|
-
return str(args[0]).encode('ASCII').hex()
|
|
584
|
-
|
|
585
|
-
def HEX_TO_INT(self, *args: list[Any]) -> str | None | int:
|
|
586
|
-
"""HEX_TO_INT Function"""
|
|
587
|
-
if len(args) > 1:
|
|
588
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
589
|
-
|
|
590
|
-
if args[0] is None:
|
|
591
|
-
return None
|
|
592
|
-
|
|
593
|
-
try:
|
|
594
|
-
return int(int(args[0], 16)) # type: ignore
|
|
595
|
-
except Exception:
|
|
596
|
-
return 'Invalid hex string'
|
|
597
|
-
|
|
598
|
-
def INT_TO_HEX(self, *args: list[Any]) -> str | None:
|
|
599
|
-
"""INT_TO_HEX Function"""
|
|
600
|
-
if len(args) > 1:
|
|
601
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
602
|
-
|
|
603
|
-
if args[0] is None:
|
|
604
|
-
return None
|
|
605
|
-
|
|
606
|
-
try:
|
|
607
|
-
return hex(int(args[0]))[2:] # type: ignore
|
|
608
|
-
except Exception:
|
|
609
|
-
return 'Invalid int value'
|
|
610
|
-
|
|
611
|
-
def TO_FLOAT(self, *args: list[Any]) -> str | None | float:
|
|
612
|
-
"""TO_FLOAT Function"""
|
|
613
|
-
if len(args) > 1:
|
|
614
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
615
|
-
|
|
616
|
-
if args[0] is None:
|
|
617
|
-
return None
|
|
618
|
-
|
|
619
|
-
try:
|
|
620
|
-
return float(args[0]) # type: ignore
|
|
621
|
-
except Exception:
|
|
622
|
-
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
623
|
-
|
|
624
|
-
def IS_PARAMETER_PRESENT(self, *args: list[Any]) -> str | bool:
|
|
625
|
-
"""IS_PARAMETER_PRESENT Function"""
|
|
626
|
-
if len(args) > 1:
|
|
627
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
628
|
-
|
|
629
|
-
if args[0] is None:
|
|
630
|
-
return None
|
|
631
|
-
|
|
632
|
-
return args[0] in self._payload # type: ignore
|
|
633
|
-
|
|
634
|
-
def IS_SENSOR_PRESENT(self, *args: list[Any]) -> str | bool:
|
|
635
|
-
"""IS_SENSOR_PRESENT Function"""
|
|
636
|
-
if len(args) > 1:
|
|
637
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
638
|
-
|
|
639
|
-
if args[0] is None:
|
|
640
|
-
return None
|
|
641
|
-
|
|
642
|
-
return args[0] in self._sensors # type: ignore
|
|
643
|
-
|
|
644
|
-
def INSIDE_RANGE(self, *args: list[Any]) -> str | None | bool:
|
|
645
|
-
"""INSIDE_RANGE Function"""
|
|
646
|
-
if len(args) != 3:
|
|
647
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
648
|
-
|
|
649
|
-
if args[0] is None or args[1] is None or args[2] is None:
|
|
650
|
-
return None
|
|
651
|
-
|
|
652
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
653
|
-
|
|
654
|
-
if not isinstance(args[0], type(args[1])):
|
|
655
|
-
return DIFFERENT_TYPES_RANGES.format(
|
|
656
|
-
arg1=type(args[0]).__name__,
|
|
657
|
-
arg2=type(args[1]).__name__,
|
|
658
|
-
arg3=type(args[2]).__name__,
|
|
659
|
-
)
|
|
660
|
-
|
|
661
|
-
return args[1] <= args[0] <= args[2]
|
|
662
|
-
|
|
663
|
-
def OUTSIDE_RANGE(self, *args: list[Any]) -> str | None | bool:
|
|
664
|
-
"""OUTSIDE_RANGE Function"""
|
|
665
|
-
if len(args) != 3:
|
|
666
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
667
|
-
|
|
668
|
-
if args[0] is None or args[1] is None or args[2] is None:
|
|
669
|
-
return None
|
|
670
|
-
|
|
671
|
-
args = self._standarize_datatypes(args) # type: ignore
|
|
672
|
-
|
|
673
|
-
if not isinstance(args[0], type(args[1])):
|
|
674
|
-
return DIFFERENT_TYPES_RANGES.format(
|
|
675
|
-
arg1=type(args[0]).__name__,
|
|
676
|
-
arg2=type(args[1]).__name__,
|
|
677
|
-
arg3=type(args[2]).__name__,
|
|
678
|
-
)
|
|
679
|
-
|
|
680
|
-
return not args[1] <= args[0] <= args[2]
|
|
681
|
-
|
|
682
|
-
def GET_TIME_DIFFERENCE(self, *args: list[Any]) -> str | float:
|
|
683
|
-
"""GET_TIME_DIFFERENCE Function"""
|
|
684
|
-
if len(args) > 0:
|
|
685
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
686
|
-
|
|
687
|
-
return self._asset_constants.get('timeElapsed', 0) # type: ignore
|
|
688
|
-
|
|
689
|
-
def IF(self, *args: list[Any]) -> Any:
|
|
690
|
-
"""IF Function"""
|
|
691
|
-
if len(args) != 3:
|
|
692
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
693
|
-
|
|
694
|
-
if args[0] is None:
|
|
695
|
-
return None
|
|
696
|
-
|
|
697
|
-
return args[1] if args[0] else args[2]
|
|
698
|
-
|
|
699
|
-
def NOW(self, *args: list[Any]) -> float:
|
|
700
|
-
"""NOW Function"""
|
|
701
|
-
import zoneinfo
|
|
702
|
-
from datetime import datetime
|
|
703
|
-
|
|
704
|
-
return datetime.now(tz=zoneinfo.ZoneInfo('UTC')).timestamp()
|
|
705
|
-
|
|
706
|
-
def REGEX(self, *args: list[Any]) -> str | None | bool:
|
|
707
|
-
"""REGEX Function"""
|
|
708
|
-
if len(args) != 2:
|
|
709
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
710
|
-
|
|
711
|
-
if args[0] is None or args[1] is None:
|
|
712
|
-
return None
|
|
713
|
-
|
|
714
|
-
if not isinstance(args[0], str):
|
|
715
|
-
return PATTERN_INVALID.format(received=type(args[0]))
|
|
716
|
-
|
|
717
|
-
import re
|
|
718
|
-
|
|
719
|
-
pattern = re.compile(args[1])
|
|
720
|
-
return bool(pattern.match(args[0]))
|
|
721
|
-
|
|
722
|
-
def IS_NONE(self, *args: list[Any]) -> str | bool:
|
|
723
|
-
"""IS_NONE Function"""
|
|
724
|
-
if len(args) != 1:
|
|
725
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
726
|
-
|
|
727
|
-
return args[0] is None
|
|
728
|
-
|
|
729
|
-
def NOT(self, *args: list[Any]) -> str | bool:
|
|
730
|
-
"""NOT Function"""
|
|
731
|
-
if len(args) != 1:
|
|
732
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
733
|
-
|
|
734
|
-
if args[0] is None:
|
|
735
|
-
return None
|
|
736
|
-
|
|
737
|
-
return not args[0]
|
|
738
|
-
|
|
739
|
-
def CONTAINS(self, *args: list[Any]) -> str | bool:
|
|
740
|
-
"""CONTAINS function"""
|
|
741
|
-
if len(args) != 2:
|
|
742
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
743
|
-
|
|
744
|
-
if args[0] is None or args[1] is None:
|
|
745
|
-
return None
|
|
746
|
-
|
|
747
|
-
return str(args[0]) in str(args[1])
|
|
748
|
-
|
|
749
|
-
def STARTS_WITH(self, *args: list[Any]) -> str | bool:
|
|
750
|
-
"""STARTS_WITH function"""
|
|
751
|
-
if len(args) != 2:
|
|
752
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
753
|
-
|
|
754
|
-
if args[0] is None or args[1] is None:
|
|
755
|
-
return None
|
|
756
|
-
|
|
757
|
-
return str(args[1]).startswith(str(args[0]))
|
|
758
|
-
|
|
759
|
-
def ENDS_WITH(self, *args: list[Any]) -> str | bool:
|
|
760
|
-
"""ENDS_WITH function"""
|
|
761
|
-
if len(args) != 2:
|
|
762
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
763
|
-
|
|
764
|
-
if args[0] is None or args[1] is None:
|
|
765
|
-
return None
|
|
766
|
-
|
|
767
|
-
return str(args[1]).endswith(str(args[0]))
|
|
768
|
-
|
|
769
|
-
def PRIMARY_DEVICE(self, *args: list[Any]) -> str:
|
|
770
|
-
"""PRIMARY_DEVICE function"""
|
|
771
|
-
if len(args) > 0:
|
|
772
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
773
|
-
|
|
774
|
-
return self._asset_constants.get('primaryDevice', None) # type: ignore
|
|
775
|
-
|
|
776
|
-
def SUBSTRING(self, *args: list[Any]) -> str:
|
|
777
|
-
"""Get a substring from string (args[0])"""
|
|
778
|
-
if len(args) < 2:
|
|
779
|
-
return INVALID_NUMBER_OF_PARAMS.format(
|
|
780
|
-
expected=2,
|
|
781
|
-
received=len(args),
|
|
782
|
-
)
|
|
783
|
-
|
|
784
|
-
if len(args) > 3:
|
|
785
|
-
return INVALID_NUMBER_OF_PARAMS.format(
|
|
786
|
-
expected=3,
|
|
787
|
-
received=len(args),
|
|
788
|
-
)
|
|
789
|
-
|
|
790
|
-
if args[0] is None:
|
|
791
|
-
return None
|
|
792
|
-
|
|
793
|
-
if not isinstance(args[0], str):
|
|
794
|
-
return DIFFERENT_TYPES.format(arg1='str', arg2=type(args[0]).__name__)
|
|
795
|
-
|
|
796
|
-
if args[1] is None:
|
|
797
|
-
return None
|
|
798
|
-
|
|
799
|
-
if not isinstance(args[1], int):
|
|
800
|
-
return DIFFERENT_TYPES.format(arg1='int', arg2=type(args[1]).__name__)
|
|
801
|
-
|
|
802
|
-
if len(args) == 3:
|
|
803
|
-
if args[2] is None:
|
|
804
|
-
return None
|
|
805
|
-
|
|
806
|
-
if not isinstance(args[2], int):
|
|
807
|
-
return DIFFERENT_TYPES.format(arg1='str', arg2=type(args[2]).__name__)
|
|
808
|
-
|
|
809
|
-
return args[0][args[1] : args[2]]
|
|
810
|
-
return args[0][args[1] :]
|
|
811
|
-
|
|
812
|
-
def UNIX_TO_STR(self, *args: list[Any]) -> str:
|
|
813
|
-
"""Convert UNIX timestamp date (args[0]) to format (args[1]) string"""
|
|
814
|
-
if len(args) < 2:
|
|
815
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
816
|
-
|
|
817
|
-
if args[0] is None:
|
|
818
|
-
return None
|
|
819
|
-
|
|
820
|
-
if args[1] is None:
|
|
821
|
-
return None
|
|
822
|
-
|
|
823
|
-
import zoneinfo
|
|
824
|
-
from datetime import datetime
|
|
825
|
-
|
|
826
|
-
tz = zoneinfo.ZoneInfo('UTC')
|
|
827
|
-
|
|
828
|
-
if len(args) > 2:
|
|
829
|
-
if args[2] is None:
|
|
830
|
-
return None
|
|
831
|
-
|
|
832
|
-
try:
|
|
833
|
-
tz = zoneinfo.ZoneInfo(args[2]) # type: ignore
|
|
834
|
-
except zoneinfo.ZoneInfoNotFoundError:
|
|
835
|
-
tz = zoneinfo.ZoneInfo('UTC')
|
|
836
|
-
|
|
837
|
-
return datetime.fromtimestamp(int(args[0]), tz=zoneinfo.ZoneInfo('UTC')).astimezone(tz).strftime(args[1]) # type: ignore
|
|
838
|
-
|
|
839
|
-
def VERSION(self, *args: list[Any]) -> str:
|
|
840
|
-
"""VERSION function"""
|
|
841
|
-
if len(args) > 0:
|
|
842
|
-
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
843
|
-
|
|
844
|
-
import importlib.metadata
|
|
845
|
-
|
|
846
|
-
version = importlib.metadata.version('layrz_sdk')
|
|
847
|
-
|
|
848
|
-
return f'LCL {version} - Layrz SDK Runtime'
|
|
1
|
+
"""Layrz Compute Language SDK"""
|
|
2
|
+
|
|
3
|
+
# ruff: noqa: ANN401
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from layrz_sdk.entities.message import PayloadType
|
|
7
|
+
|
|
8
|
+
PATTERN_INVALID = 'Pattern should be string, received {received}'
|
|
9
|
+
INVALID_NUMBER_OF_PARAMS = 'Invalid number of arguments - Expected {expected} - Given {received}'
|
|
10
|
+
DIFFERENT_TYPES_RANGES = 'Invalid data range, value: {arg1} - Minimum: {arg2} - Maximum: {arg3}'
|
|
11
|
+
DIFFERENT_TYPES = 'Invalid data types - arg1: {arg1} - arg2: {arg2}'
|
|
12
|
+
INVALID_ARGUMENTS = 'Invalid arguments - {e}'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LclCore:
|
|
16
|
+
"""Layrz Compute Language SDK"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
script: str = '',
|
|
21
|
+
sensors: Optional[PayloadType] = None,
|
|
22
|
+
previous_sensors: Optional[PayloadType] = None,
|
|
23
|
+
payload: Optional[PayloadType] = None,
|
|
24
|
+
asset_constants: Optional[dict[str, Any]] = None,
|
|
25
|
+
custom_fields: Optional[dict[str, Any]] = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Creates a new instance of LclCore
|
|
29
|
+
|
|
30
|
+
:param script: Script to be executed
|
|
31
|
+
:type script: str
|
|
32
|
+
:param sensors: Sensors dictionary
|
|
33
|
+
:type sensors: Optional[PayloadType]
|
|
34
|
+
:param previous_sensors: Previous sensors dictionary
|
|
35
|
+
:type previous_sensors: Optional[PayloadType]
|
|
36
|
+
:param payload: Payload dictionary
|
|
37
|
+
:type payload: Optional[PayloadType]
|
|
38
|
+
:param asset_constants: Asset constants dictionary
|
|
39
|
+
:type asset_constants: Optional[dict[str, Any]]
|
|
40
|
+
:param custom_fields: Custom fields dictionary
|
|
41
|
+
:type custom_fields: Optional[dict[str, Any]]
|
|
42
|
+
|
|
43
|
+
:return: None
|
|
44
|
+
:rtype: None
|
|
45
|
+
"""
|
|
46
|
+
if sensors is None:
|
|
47
|
+
sensors = {}
|
|
48
|
+
|
|
49
|
+
self._sensors = sensors
|
|
50
|
+
if previous_sensors is None:
|
|
51
|
+
previous_sensors = {}
|
|
52
|
+
self._previous_sensors = previous_sensors
|
|
53
|
+
if payload is None:
|
|
54
|
+
payload = {}
|
|
55
|
+
self._payload = payload
|
|
56
|
+
|
|
57
|
+
if asset_constants is None:
|
|
58
|
+
asset_constants = {}
|
|
59
|
+
self._asset_constants = asset_constants
|
|
60
|
+
|
|
61
|
+
if custom_fields is None:
|
|
62
|
+
custom_fields = {}
|
|
63
|
+
self._custom_fields = custom_fields
|
|
64
|
+
|
|
65
|
+
self._script = script
|
|
66
|
+
|
|
67
|
+
def perform(
|
|
68
|
+
self,
|
|
69
|
+
additional_globals: Optional[dict[str, Any]] = None,
|
|
70
|
+
additional_locals: Optional[dict[str, Any]] = None,
|
|
71
|
+
) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Perform script using Layrz Compute Language
|
|
74
|
+
|
|
75
|
+
:param additional_globals: Additional global variables
|
|
76
|
+
:type additional_globals: Optional[dict[str, Any]]
|
|
77
|
+
:param additional_locals: Additional local variables
|
|
78
|
+
:type additional_locals: Optional[dict[str, Any]]
|
|
79
|
+
|
|
80
|
+
:return: Result of the script in JSON format
|
|
81
|
+
:rtype: str
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
local_variables = {
|
|
85
|
+
'payload': self._payload,
|
|
86
|
+
'sensors': self._sensors,
|
|
87
|
+
'custom_fields': self._custom_fields,
|
|
88
|
+
'previous_sensors': self._previous_sensors,
|
|
89
|
+
'asset_constants': self._asset_constants,
|
|
90
|
+
}
|
|
91
|
+
if additional_locals:
|
|
92
|
+
local_variables.update(additional_locals)
|
|
93
|
+
|
|
94
|
+
## Define global variables and functions
|
|
95
|
+
global_functions = {
|
|
96
|
+
'GET_PARAM': self.GET_PARAM,
|
|
97
|
+
'GET_SENSOR': self.GET_SENSOR,
|
|
98
|
+
'CONSTANT': self.CONSTANT,
|
|
99
|
+
'GET_CUSTOM_FIELD': self.GET_CUSTOM_FIELD,
|
|
100
|
+
'COMPARE': self.COMPARE,
|
|
101
|
+
'OR_OPERATOR': self.OR_OPERATOR,
|
|
102
|
+
'AND_OPERATOR': self.AND_OPERATOR,
|
|
103
|
+
'SUM': self.SUM,
|
|
104
|
+
'SUBSTRACT': self.SUBSTRACT,
|
|
105
|
+
'MULTIPLY': self.MULTIPLY,
|
|
106
|
+
'DIVIDE': self.DIVIDE,
|
|
107
|
+
'TO_BOOL': self.TO_BOOL,
|
|
108
|
+
'TO_STR': self.TO_STR,
|
|
109
|
+
'TO_INT': self.TO_INT,
|
|
110
|
+
'CEIL': self.CEIL,
|
|
111
|
+
'FLOOR': self.FLOOR,
|
|
112
|
+
'ROUND': self.ROUND,
|
|
113
|
+
'SQRT': self.SQRT,
|
|
114
|
+
'CONCAT': self.CONCAT,
|
|
115
|
+
'NOW': self.NOW,
|
|
116
|
+
'RANDOM': self.RANDOM,
|
|
117
|
+
'RANDOM_INT': self.RANDOM_INT,
|
|
118
|
+
'GREATER_THAN_OR_EQUALS_TO': self.GREATER_THAN_OR_EQUALS_TO,
|
|
119
|
+
'GREATER_THAN': self.GREATER_THAN,
|
|
120
|
+
'LESS_THAN_OR_EQUALS_TO': self.LESS_THAN_OR_EQUALS_TO,
|
|
121
|
+
'LESS_THAN': self.LESS_THAN,
|
|
122
|
+
'DIFFERENT': self.DIFFERENT,
|
|
123
|
+
'HEX_TO_STR': self.HEX_TO_STR,
|
|
124
|
+
'STR_TO_HEX': self.STR_TO_HEX,
|
|
125
|
+
'HEX_TO_INT': self.HEX_TO_INT,
|
|
126
|
+
'INT_TO_HEX': self.INT_TO_HEX,
|
|
127
|
+
'TO_FLOAT': self.TO_FLOAT,
|
|
128
|
+
'GET_DISTANCE_TRAVELED': self.GET_DISTANCE_TRAVELED,
|
|
129
|
+
'GET_PREVIOUS_SENSOR': self.GET_PREVIOUS_SENSOR,
|
|
130
|
+
'IS_PARAMETER_PRESENT': self.IS_PARAMETER_PRESENT,
|
|
131
|
+
'IS_SENSOR_PRESENT': self.IS_SENSOR_PRESENT,
|
|
132
|
+
'INSIDE_RANGE': self.INSIDE_RANGE,
|
|
133
|
+
'OUTSIDE_RANGE': self.OUTSIDE_RANGE,
|
|
134
|
+
'GET_TIME_DIFFERENCE': self.GET_TIME_DIFFERENCE,
|
|
135
|
+
'IF': self.IF,
|
|
136
|
+
'REGEX': self.REGEX,
|
|
137
|
+
'IS_NONE': self.IS_NONE,
|
|
138
|
+
'NOT': self.NOT,
|
|
139
|
+
'CONTAINS': self.CONTAINS,
|
|
140
|
+
'STARTS_WITH': self.STARTS_WITH,
|
|
141
|
+
'ENDS_WITH': self.ENDS_WITH,
|
|
142
|
+
'PRIMARY_DEVICE': self.PRIMARY_DEVICE,
|
|
143
|
+
'SUBSTRING': self.SUBSTRING,
|
|
144
|
+
'UNIX_TO_STR': self.UNIX_TO_STR,
|
|
145
|
+
'VERSION': self.VERSION,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if additional_globals:
|
|
149
|
+
global_functions.update(additional_globals)
|
|
150
|
+
|
|
151
|
+
import json
|
|
152
|
+
|
|
153
|
+
result = json.dumps(eval(self._script, global_functions, local_variables))
|
|
154
|
+
|
|
155
|
+
return result
|
|
156
|
+
except Exception as err:
|
|
157
|
+
import json
|
|
158
|
+
|
|
159
|
+
return json.dumps(INVALID_ARGUMENTS.format(e=err))
|
|
160
|
+
|
|
161
|
+
def _standarize_datatypes(self, args: list[Any]) -> list[Any]:
|
|
162
|
+
"""Standarize data types"""
|
|
163
|
+
result_args = []
|
|
164
|
+
|
|
165
|
+
for arg in args:
|
|
166
|
+
if isinstance(arg, (float, int)):
|
|
167
|
+
result_args.append(float(arg))
|
|
168
|
+
else:
|
|
169
|
+
result_args.append(arg)
|
|
170
|
+
|
|
171
|
+
return result_args
|
|
172
|
+
|
|
173
|
+
def GET_PARAM(self, *args: list[Any]) -> Any:
|
|
174
|
+
"""GET_PARAM Function"""
|
|
175
|
+
if len(args) > 2:
|
|
176
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
177
|
+
|
|
178
|
+
if args[0] is None:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
if len(args) > 1:
|
|
182
|
+
return self._payload.get(args[0], args[1]) # type: ignore
|
|
183
|
+
return self._payload.get(args[0], None) # type: ignore
|
|
184
|
+
|
|
185
|
+
def GET_DISTANCE_TRAVELED(self, *args: list[Any]) -> str | float:
|
|
186
|
+
"""GET_DISTANCE_TRAVELED Function"""
|
|
187
|
+
if len(args) > 0:
|
|
188
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
189
|
+
return self._asset_constants.get('distanceTraveled', 0) # type: ignore
|
|
190
|
+
|
|
191
|
+
def GET_PREVIOUS_SENSOR(self, *args: list[Any]) -> Any:
|
|
192
|
+
"""GET_PREVIOUS_SENSOR Function"""
|
|
193
|
+
if len(args) < 1:
|
|
194
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
195
|
+
|
|
196
|
+
if len(args) > 2:
|
|
197
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
198
|
+
|
|
199
|
+
if args[0] is None:
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
if len(args) > 1:
|
|
203
|
+
return self._previous_sensors.get(args[0], args[1]) # type: ignore
|
|
204
|
+
return self._previous_sensors.get(args[0], None) # type: ignore
|
|
205
|
+
|
|
206
|
+
def GET_SENSOR(self, *args: list[Any]) -> Any:
|
|
207
|
+
"""GET_SENSOR Function"""
|
|
208
|
+
if len(args) < 1:
|
|
209
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
210
|
+
|
|
211
|
+
if len(args) > 2:
|
|
212
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
213
|
+
|
|
214
|
+
if args[0] is None:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
if len(args) > 1:
|
|
218
|
+
return self._sensors.get(args[0], args[1]) # type: ignore
|
|
219
|
+
return self._sensors.get(args[0], None) # type: ignore
|
|
220
|
+
|
|
221
|
+
def CONSTANT(self, *args: list[Any]) -> Any:
|
|
222
|
+
"""CONSTANT Function"""
|
|
223
|
+
if len(args) > 1:
|
|
224
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
225
|
+
return args[0]
|
|
226
|
+
|
|
227
|
+
def GET_CUSTOM_FIELD(self, *args: list[Any]) -> str:
|
|
228
|
+
"""GET_CUSTOM_FIELD Function"""
|
|
229
|
+
if len(args) > 2:
|
|
230
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
231
|
+
|
|
232
|
+
if args[0] is None:
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
if len(args) > 1:
|
|
236
|
+
return self._custom_fields.get(args[0], args[1]) # type: ignore
|
|
237
|
+
|
|
238
|
+
return self._custom_fields.get(args[0], '') # type: ignore
|
|
239
|
+
|
|
240
|
+
def COMPARE(self, *args: list[Any]) -> str | None | bool:
|
|
241
|
+
"""COMPARE Function"""
|
|
242
|
+
if len(args) != 2:
|
|
243
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
244
|
+
|
|
245
|
+
if args[0] is None or args[1] is None:
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
249
|
+
|
|
250
|
+
if not isinstance(args[0], type(args[1])):
|
|
251
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
252
|
+
return args[0] == args[1]
|
|
253
|
+
|
|
254
|
+
def OR_OPERATOR(self, *args: list[Any]) -> bool:
|
|
255
|
+
"""OR_OPERATOR Function"""
|
|
256
|
+
result = False
|
|
257
|
+
|
|
258
|
+
for val in args:
|
|
259
|
+
if val is None:
|
|
260
|
+
return None
|
|
261
|
+
if isinstance(val, bool):
|
|
262
|
+
result = result or val
|
|
263
|
+
|
|
264
|
+
return result
|
|
265
|
+
|
|
266
|
+
def AND_OPERATOR(self, *args: list[Any]) -> bool:
|
|
267
|
+
"""AND_OPERATOR Function"""
|
|
268
|
+
result = False
|
|
269
|
+
is_first = True
|
|
270
|
+
|
|
271
|
+
for val in args:
|
|
272
|
+
if val is None:
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
if is_first:
|
|
276
|
+
is_first = False
|
|
277
|
+
result = val # type: ignore
|
|
278
|
+
elif isinstance(val, bool):
|
|
279
|
+
result = result and val
|
|
280
|
+
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
def SUM(self, *args: list[Any]) -> float:
|
|
284
|
+
"""SUM Function"""
|
|
285
|
+
result = 0
|
|
286
|
+
|
|
287
|
+
for num in args:
|
|
288
|
+
if num is None:
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
result += float(num) # type: ignore
|
|
293
|
+
except Exception:
|
|
294
|
+
pass
|
|
295
|
+
|
|
296
|
+
return result
|
|
297
|
+
|
|
298
|
+
def SUBSTRACT(self, *args: list[Any]) -> float:
|
|
299
|
+
"""SUBSTRACT Function"""
|
|
300
|
+
result = 0
|
|
301
|
+
is_first = True
|
|
302
|
+
|
|
303
|
+
for num in args:
|
|
304
|
+
if num is None:
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
if is_first:
|
|
309
|
+
result = float(num) # type: ignore
|
|
310
|
+
is_first = False
|
|
311
|
+
else:
|
|
312
|
+
result -= float(num) # type: ignore
|
|
313
|
+
except Exception:
|
|
314
|
+
pass
|
|
315
|
+
|
|
316
|
+
return result
|
|
317
|
+
|
|
318
|
+
def MULTIPLY(self, *args: list[Any]) -> float:
|
|
319
|
+
"""MULTIPLY Function"""
|
|
320
|
+
result = 0
|
|
321
|
+
is_first = True
|
|
322
|
+
|
|
323
|
+
for num in args:
|
|
324
|
+
if num is None:
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
if is_first:
|
|
329
|
+
is_first = False
|
|
330
|
+
result = float(num) # type: ignore
|
|
331
|
+
else:
|
|
332
|
+
result *= float(num) # type: ignore
|
|
333
|
+
except Exception:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
return result
|
|
337
|
+
|
|
338
|
+
def DIVIDE(self, *args: list[Any]) -> float:
|
|
339
|
+
"""DIVIDE Function"""
|
|
340
|
+
result = 0
|
|
341
|
+
is_first = True
|
|
342
|
+
|
|
343
|
+
for num in args:
|
|
344
|
+
if num is None:
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
if is_first:
|
|
349
|
+
is_first = False
|
|
350
|
+
result = float(num) # type: ignore
|
|
351
|
+
else:
|
|
352
|
+
result /= float(num) # type: ignore
|
|
353
|
+
except Exception:
|
|
354
|
+
pass
|
|
355
|
+
|
|
356
|
+
return result
|
|
357
|
+
|
|
358
|
+
def TO_BOOL(self, *args: list[Any]) -> str | None | bool:
|
|
359
|
+
"""TO_BOOL Function"""
|
|
360
|
+
if len(args) > 1:
|
|
361
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
362
|
+
|
|
363
|
+
if args[0] is None:
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
return bool(args[0])
|
|
367
|
+
|
|
368
|
+
def TO_STR(self, *args: list[Any]) -> str | None:
|
|
369
|
+
"""TO_STR Function"""
|
|
370
|
+
if len(args) > 1:
|
|
371
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
372
|
+
|
|
373
|
+
if args[0] is None:
|
|
374
|
+
return None
|
|
375
|
+
|
|
376
|
+
return str(args[0])
|
|
377
|
+
|
|
378
|
+
def TO_INT(self, *args: list[Any]) -> str | None | int:
|
|
379
|
+
"""TO_INT Function"""
|
|
380
|
+
if len(args) > 1:
|
|
381
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
382
|
+
|
|
383
|
+
if args[0] is None:
|
|
384
|
+
return None
|
|
385
|
+
|
|
386
|
+
return int(args[0]) # type: ignore
|
|
387
|
+
|
|
388
|
+
def CEIL(self, *args: list[Any]) -> str | None | int:
|
|
389
|
+
"""CEIL Function"""
|
|
390
|
+
if len(args) > 1:
|
|
391
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
392
|
+
|
|
393
|
+
if args[0] is None:
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
if not isinstance(args[0], (int, float)):
|
|
397
|
+
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
398
|
+
|
|
399
|
+
import math
|
|
400
|
+
|
|
401
|
+
return math.ceil(args[0])
|
|
402
|
+
|
|
403
|
+
def FLOOR(self, *args: list[Any]) -> str | None | int:
|
|
404
|
+
"""FLOOR Function"""
|
|
405
|
+
if len(args) > 1:
|
|
406
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
407
|
+
|
|
408
|
+
if args[0] is None:
|
|
409
|
+
return None
|
|
410
|
+
|
|
411
|
+
if not isinstance(args[0], (int, float)):
|
|
412
|
+
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
413
|
+
|
|
414
|
+
import math
|
|
415
|
+
|
|
416
|
+
return math.floor(args[0])
|
|
417
|
+
|
|
418
|
+
def ROUND(self, *args: list[Any]) -> str | None | int:
|
|
419
|
+
"""ROUND Function"""
|
|
420
|
+
if len(args) > 1:
|
|
421
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
422
|
+
|
|
423
|
+
if args[0] is None:
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
if not isinstance(args[0], (int, float)):
|
|
427
|
+
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
428
|
+
|
|
429
|
+
return round(args[0])
|
|
430
|
+
|
|
431
|
+
def SQRT(self, *args: list[Any]) -> str | None | float:
|
|
432
|
+
"""SQRT Function"""
|
|
433
|
+
if len(args) > 1:
|
|
434
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
435
|
+
|
|
436
|
+
if args[0] is None:
|
|
437
|
+
return None
|
|
438
|
+
|
|
439
|
+
import math
|
|
440
|
+
|
|
441
|
+
return math.sqrt(args[0]) # type: ignore
|
|
442
|
+
|
|
443
|
+
def CONCAT(self, *args: list[Any]) -> str | None:
|
|
444
|
+
"""CONCAT Function"""
|
|
445
|
+
for val in args:
|
|
446
|
+
if val is None:
|
|
447
|
+
return None
|
|
448
|
+
|
|
449
|
+
return ''.join([str(val) for val in args])
|
|
450
|
+
|
|
451
|
+
def RANDOM(self, *args: list[Any]) -> float | str:
|
|
452
|
+
"""RANDOM Function"""
|
|
453
|
+
if len(args) > 2:
|
|
454
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
455
|
+
if len(args) < 2:
|
|
456
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
457
|
+
|
|
458
|
+
if args[0] is None or args[1] is None:
|
|
459
|
+
return None
|
|
460
|
+
|
|
461
|
+
import random
|
|
462
|
+
|
|
463
|
+
return random.random() * (float(args[1]) - float(args[0])) + float(args[0]) # type: ignore
|
|
464
|
+
|
|
465
|
+
def RANDOM_INT(self, *args: list[Any]) -> int | str:
|
|
466
|
+
"""RANDOM_INT Function"""
|
|
467
|
+
if len(args) != 2:
|
|
468
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
469
|
+
|
|
470
|
+
if args[0] is None or args[1] is None:
|
|
471
|
+
return None
|
|
472
|
+
|
|
473
|
+
import random
|
|
474
|
+
|
|
475
|
+
return random.randint(int(args[0]), int(args[1])) # type: ignore
|
|
476
|
+
|
|
477
|
+
def GREATER_THAN_OR_EQUALS_TO(self, *args: list[Any]) -> str | None | bool:
|
|
478
|
+
"""GREATER_THAN_OR_EQUALS_TO Function"""
|
|
479
|
+
if len(args) > 2:
|
|
480
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
481
|
+
if len(args) < 2:
|
|
482
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
483
|
+
|
|
484
|
+
if args[0] is None or args[1] is None:
|
|
485
|
+
return None
|
|
486
|
+
|
|
487
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
488
|
+
|
|
489
|
+
if not isinstance(args[0], type(args[1])):
|
|
490
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
491
|
+
return args[0] >= args[1]
|
|
492
|
+
|
|
493
|
+
def GREATER_THAN(self, *args: list[Any]) -> str | None | bool:
|
|
494
|
+
"""GREATER_THAN Function"""
|
|
495
|
+
if len(args) > 2:
|
|
496
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
497
|
+
if len(args) < 2:
|
|
498
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
499
|
+
|
|
500
|
+
if args[0] is None or args[1] is None:
|
|
501
|
+
return None
|
|
502
|
+
|
|
503
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
504
|
+
|
|
505
|
+
if not isinstance(args[0], type(args[1])):
|
|
506
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
507
|
+
return args[0] > args[1]
|
|
508
|
+
|
|
509
|
+
def LESS_THAN_OR_EQUALS_TO(self, *args: list[Any]) -> str | None | bool:
|
|
510
|
+
"""LESS_THAN_OR_EQUALS_TO Function"""
|
|
511
|
+
if len(args) > 2:
|
|
512
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
513
|
+
if len(args) < 2:
|
|
514
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
515
|
+
|
|
516
|
+
if args[0] is None or args[1] is None:
|
|
517
|
+
return None
|
|
518
|
+
|
|
519
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
520
|
+
|
|
521
|
+
if not isinstance(args[0], type(args[1])):
|
|
522
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
523
|
+
return args[0] <= args[1]
|
|
524
|
+
|
|
525
|
+
def LESS_THAN(self, *args: list[Any]) -> str | None | bool:
|
|
526
|
+
"""LESS_THAN Function"""
|
|
527
|
+
if len(args) > 2:
|
|
528
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
529
|
+
if len(args) < 2:
|
|
530
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
531
|
+
|
|
532
|
+
if args[0] is None or args[1] is None:
|
|
533
|
+
return None
|
|
534
|
+
|
|
535
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
536
|
+
|
|
537
|
+
if not isinstance(args[0], type(args[1])):
|
|
538
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
539
|
+
return args[0] < args[1]
|
|
540
|
+
|
|
541
|
+
def DIFFERENT(self, *args: list[Any]) -> str | None | bool:
|
|
542
|
+
"""DIFFERENT Function"""
|
|
543
|
+
if len(args) > 2:
|
|
544
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
545
|
+
if len(args) < 2:
|
|
546
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
547
|
+
|
|
548
|
+
if args[0] is None or args[1] is None:
|
|
549
|
+
return None
|
|
550
|
+
|
|
551
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
552
|
+
|
|
553
|
+
if not isinstance(args[0], type(args[1])):
|
|
554
|
+
return DIFFERENT_TYPES.format(arg1=type(args[0]).__name__, arg2=type(args[1]).__name__)
|
|
555
|
+
return args[0] != args[1]
|
|
556
|
+
|
|
557
|
+
def HEX_TO_STR(self, *args: list[Any]) -> str | None:
|
|
558
|
+
"""HEX_TO_STR Function"""
|
|
559
|
+
if len(args) > 1:
|
|
560
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
561
|
+
|
|
562
|
+
if args[0] is None:
|
|
563
|
+
return None
|
|
564
|
+
|
|
565
|
+
hexa = args[0]
|
|
566
|
+
if hexa.startswith('0x'): # type: ignore
|
|
567
|
+
hexa = hexa[2:]
|
|
568
|
+
|
|
569
|
+
try:
|
|
570
|
+
byte_array = bytes.fromhex(hexa) # type: ignore
|
|
571
|
+
return byte_array.decode('ASCII')
|
|
572
|
+
except Exception:
|
|
573
|
+
return 'Invalid hex string'
|
|
574
|
+
|
|
575
|
+
def STR_TO_HEX(self, *args: list[Any]) -> str | None:
|
|
576
|
+
"""STR_TO_HEX Function"""
|
|
577
|
+
if len(args) > 1:
|
|
578
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
579
|
+
|
|
580
|
+
if args[0] is None:
|
|
581
|
+
return None
|
|
582
|
+
|
|
583
|
+
return str(args[0]).encode('ASCII').hex()
|
|
584
|
+
|
|
585
|
+
def HEX_TO_INT(self, *args: list[Any]) -> str | None | int:
|
|
586
|
+
"""HEX_TO_INT Function"""
|
|
587
|
+
if len(args) > 1:
|
|
588
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
589
|
+
|
|
590
|
+
if args[0] is None:
|
|
591
|
+
return None
|
|
592
|
+
|
|
593
|
+
try:
|
|
594
|
+
return int(int(args[0], 16)) # type: ignore
|
|
595
|
+
except Exception:
|
|
596
|
+
return 'Invalid hex string'
|
|
597
|
+
|
|
598
|
+
def INT_TO_HEX(self, *args: list[Any]) -> str | None:
|
|
599
|
+
"""INT_TO_HEX Function"""
|
|
600
|
+
if len(args) > 1:
|
|
601
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
602
|
+
|
|
603
|
+
if args[0] is None:
|
|
604
|
+
return None
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
return hex(int(args[0]))[2:] # type: ignore
|
|
608
|
+
except Exception:
|
|
609
|
+
return 'Invalid int value'
|
|
610
|
+
|
|
611
|
+
def TO_FLOAT(self, *args: list[Any]) -> str | None | float:
|
|
612
|
+
"""TO_FLOAT Function"""
|
|
613
|
+
if len(args) > 1:
|
|
614
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
615
|
+
|
|
616
|
+
if args[0] is None:
|
|
617
|
+
return None
|
|
618
|
+
|
|
619
|
+
try:
|
|
620
|
+
return float(args[0]) # type: ignore
|
|
621
|
+
except Exception:
|
|
622
|
+
return f'Invalid arguments - must be real number, not {type(args[0]).__name__}'
|
|
623
|
+
|
|
624
|
+
def IS_PARAMETER_PRESENT(self, *args: list[Any]) -> str | bool:
|
|
625
|
+
"""IS_PARAMETER_PRESENT Function"""
|
|
626
|
+
if len(args) > 1:
|
|
627
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
628
|
+
|
|
629
|
+
if args[0] is None:
|
|
630
|
+
return None
|
|
631
|
+
|
|
632
|
+
return args[0] in self._payload # type: ignore
|
|
633
|
+
|
|
634
|
+
def IS_SENSOR_PRESENT(self, *args: list[Any]) -> str | bool:
|
|
635
|
+
"""IS_SENSOR_PRESENT Function"""
|
|
636
|
+
if len(args) > 1:
|
|
637
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
638
|
+
|
|
639
|
+
if args[0] is None:
|
|
640
|
+
return None
|
|
641
|
+
|
|
642
|
+
return args[0] in self._sensors # type: ignore
|
|
643
|
+
|
|
644
|
+
def INSIDE_RANGE(self, *args: list[Any]) -> str | None | bool:
|
|
645
|
+
"""INSIDE_RANGE Function"""
|
|
646
|
+
if len(args) != 3:
|
|
647
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
648
|
+
|
|
649
|
+
if args[0] is None or args[1] is None or args[2] is None:
|
|
650
|
+
return None
|
|
651
|
+
|
|
652
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
653
|
+
|
|
654
|
+
if not isinstance(args[0], type(args[1])):
|
|
655
|
+
return DIFFERENT_TYPES_RANGES.format(
|
|
656
|
+
arg1=type(args[0]).__name__,
|
|
657
|
+
arg2=type(args[1]).__name__,
|
|
658
|
+
arg3=type(args[2]).__name__,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
return args[1] <= args[0] <= args[2]
|
|
662
|
+
|
|
663
|
+
def OUTSIDE_RANGE(self, *args: list[Any]) -> str | None | bool:
|
|
664
|
+
"""OUTSIDE_RANGE Function"""
|
|
665
|
+
if len(args) != 3:
|
|
666
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
667
|
+
|
|
668
|
+
if args[0] is None or args[1] is None or args[2] is None:
|
|
669
|
+
return None
|
|
670
|
+
|
|
671
|
+
args = self._standarize_datatypes(args) # type: ignore
|
|
672
|
+
|
|
673
|
+
if not isinstance(args[0], type(args[1])):
|
|
674
|
+
return DIFFERENT_TYPES_RANGES.format(
|
|
675
|
+
arg1=type(args[0]).__name__,
|
|
676
|
+
arg2=type(args[1]).__name__,
|
|
677
|
+
arg3=type(args[2]).__name__,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
return not args[1] <= args[0] <= args[2]
|
|
681
|
+
|
|
682
|
+
def GET_TIME_DIFFERENCE(self, *args: list[Any]) -> str | float:
|
|
683
|
+
"""GET_TIME_DIFFERENCE Function"""
|
|
684
|
+
if len(args) > 0:
|
|
685
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
686
|
+
|
|
687
|
+
return self._asset_constants.get('timeElapsed', 0) # type: ignore
|
|
688
|
+
|
|
689
|
+
def IF(self, *args: list[Any]) -> Any:
|
|
690
|
+
"""IF Function"""
|
|
691
|
+
if len(args) != 3:
|
|
692
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=3, received=len(args))
|
|
693
|
+
|
|
694
|
+
if args[0] is None:
|
|
695
|
+
return None
|
|
696
|
+
|
|
697
|
+
return args[1] if args[0] else args[2]
|
|
698
|
+
|
|
699
|
+
def NOW(self, *args: list[Any]) -> float:
|
|
700
|
+
"""NOW Function"""
|
|
701
|
+
import zoneinfo
|
|
702
|
+
from datetime import datetime
|
|
703
|
+
|
|
704
|
+
return datetime.now(tz=zoneinfo.ZoneInfo('UTC')).timestamp()
|
|
705
|
+
|
|
706
|
+
def REGEX(self, *args: list[Any]) -> str | None | bool:
|
|
707
|
+
"""REGEX Function"""
|
|
708
|
+
if len(args) != 2:
|
|
709
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
710
|
+
|
|
711
|
+
if args[0] is None or args[1] is None:
|
|
712
|
+
return None
|
|
713
|
+
|
|
714
|
+
if not isinstance(args[0], str):
|
|
715
|
+
return PATTERN_INVALID.format(received=type(args[0]))
|
|
716
|
+
|
|
717
|
+
import re
|
|
718
|
+
|
|
719
|
+
pattern = re.compile(args[1])
|
|
720
|
+
return bool(pattern.match(args[0]))
|
|
721
|
+
|
|
722
|
+
def IS_NONE(self, *args: list[Any]) -> str | bool:
|
|
723
|
+
"""IS_NONE Function"""
|
|
724
|
+
if len(args) != 1:
|
|
725
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
726
|
+
|
|
727
|
+
return args[0] is None
|
|
728
|
+
|
|
729
|
+
def NOT(self, *args: list[Any]) -> str | bool:
|
|
730
|
+
"""NOT Function"""
|
|
731
|
+
if len(args) != 1:
|
|
732
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=1, received=len(args))
|
|
733
|
+
|
|
734
|
+
if args[0] is None:
|
|
735
|
+
return None
|
|
736
|
+
|
|
737
|
+
return not args[0]
|
|
738
|
+
|
|
739
|
+
def CONTAINS(self, *args: list[Any]) -> str | bool:
|
|
740
|
+
"""CONTAINS function"""
|
|
741
|
+
if len(args) != 2:
|
|
742
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
743
|
+
|
|
744
|
+
if args[0] is None or args[1] is None:
|
|
745
|
+
return None
|
|
746
|
+
|
|
747
|
+
return str(args[0]) in str(args[1])
|
|
748
|
+
|
|
749
|
+
def STARTS_WITH(self, *args: list[Any]) -> str | bool:
|
|
750
|
+
"""STARTS_WITH function"""
|
|
751
|
+
if len(args) != 2:
|
|
752
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
753
|
+
|
|
754
|
+
if args[0] is None or args[1] is None:
|
|
755
|
+
return None
|
|
756
|
+
|
|
757
|
+
return str(args[1]).startswith(str(args[0]))
|
|
758
|
+
|
|
759
|
+
def ENDS_WITH(self, *args: list[Any]) -> str | bool:
|
|
760
|
+
"""ENDS_WITH function"""
|
|
761
|
+
if len(args) != 2:
|
|
762
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
763
|
+
|
|
764
|
+
if args[0] is None or args[1] is None:
|
|
765
|
+
return None
|
|
766
|
+
|
|
767
|
+
return str(args[1]).endswith(str(args[0]))
|
|
768
|
+
|
|
769
|
+
def PRIMARY_DEVICE(self, *args: list[Any]) -> str:
|
|
770
|
+
"""PRIMARY_DEVICE function"""
|
|
771
|
+
if len(args) > 0:
|
|
772
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
773
|
+
|
|
774
|
+
return self._asset_constants.get('primaryDevice', None) # type: ignore
|
|
775
|
+
|
|
776
|
+
def SUBSTRING(self, *args: list[Any]) -> str:
|
|
777
|
+
"""Get a substring from string (args[0])"""
|
|
778
|
+
if len(args) < 2:
|
|
779
|
+
return INVALID_NUMBER_OF_PARAMS.format(
|
|
780
|
+
expected=2,
|
|
781
|
+
received=len(args),
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
if len(args) > 3:
|
|
785
|
+
return INVALID_NUMBER_OF_PARAMS.format(
|
|
786
|
+
expected=3,
|
|
787
|
+
received=len(args),
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
if args[0] is None:
|
|
791
|
+
return None
|
|
792
|
+
|
|
793
|
+
if not isinstance(args[0], str):
|
|
794
|
+
return DIFFERENT_TYPES.format(arg1='str', arg2=type(args[0]).__name__)
|
|
795
|
+
|
|
796
|
+
if args[1] is None:
|
|
797
|
+
return None
|
|
798
|
+
|
|
799
|
+
if not isinstance(args[1], int):
|
|
800
|
+
return DIFFERENT_TYPES.format(arg1='int', arg2=type(args[1]).__name__)
|
|
801
|
+
|
|
802
|
+
if len(args) == 3:
|
|
803
|
+
if args[2] is None:
|
|
804
|
+
return None
|
|
805
|
+
|
|
806
|
+
if not isinstance(args[2], int):
|
|
807
|
+
return DIFFERENT_TYPES.format(arg1='str', arg2=type(args[2]).__name__)
|
|
808
|
+
|
|
809
|
+
return args[0][args[1] : args[2]]
|
|
810
|
+
return args[0][args[1] :]
|
|
811
|
+
|
|
812
|
+
def UNIX_TO_STR(self, *args: list[Any]) -> str:
|
|
813
|
+
"""Convert UNIX timestamp date (args[0]) to format (args[1]) string"""
|
|
814
|
+
if len(args) < 2:
|
|
815
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=2, received=len(args))
|
|
816
|
+
|
|
817
|
+
if args[0] is None:
|
|
818
|
+
return None
|
|
819
|
+
|
|
820
|
+
if args[1] is None:
|
|
821
|
+
return None
|
|
822
|
+
|
|
823
|
+
import zoneinfo
|
|
824
|
+
from datetime import datetime
|
|
825
|
+
|
|
826
|
+
tz = zoneinfo.ZoneInfo('UTC')
|
|
827
|
+
|
|
828
|
+
if len(args) > 2:
|
|
829
|
+
if args[2] is None:
|
|
830
|
+
return None
|
|
831
|
+
|
|
832
|
+
try:
|
|
833
|
+
tz = zoneinfo.ZoneInfo(args[2]) # type: ignore
|
|
834
|
+
except zoneinfo.ZoneInfoNotFoundError:
|
|
835
|
+
tz = zoneinfo.ZoneInfo('UTC')
|
|
836
|
+
|
|
837
|
+
return datetime.fromtimestamp(int(args[0]), tz=zoneinfo.ZoneInfo('UTC')).astimezone(tz).strftime(args[1]) # type: ignore
|
|
838
|
+
|
|
839
|
+
def VERSION(self, *args: list[Any]) -> str:
|
|
840
|
+
"""VERSION function"""
|
|
841
|
+
if len(args) > 0:
|
|
842
|
+
return INVALID_NUMBER_OF_PARAMS.format(expected=0, received=len(args))
|
|
843
|
+
|
|
844
|
+
import importlib.metadata
|
|
845
|
+
|
|
846
|
+
version = importlib.metadata.version('layrz_sdk')
|
|
847
|
+
|
|
848
|
+
return f'LCL {version} - Layrz SDK Runtime'
|