@robdobsn/raftjs 1.8.5 → 1.10.7

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.
Files changed (224) hide show
  1. package/.editorconfig +14 -0
  2. package/.gitattributes +11 -0
  3. package/.nvmrc +1 -0
  4. package/TODO.md +1 -0
  5. package/dist/react-native/RaftAttributeHandler.d.ts +14 -0
  6. package/dist/react-native/RaftAttributeHandler.js +375 -0
  7. package/dist/react-native/RaftAttributeHandler.js.map +1 -0
  8. package/dist/react-native/RaftChannel.d.ts +20 -0
  9. package/dist/react-native/RaftChannel.js +12 -0
  10. package/dist/react-native/RaftChannel.js.map +1 -0
  11. package/dist/react-native/RaftChannelBLE.native.d.ts +95 -0
  12. package/dist/react-native/RaftChannelBLE.native.js +483 -0
  13. package/dist/react-native/RaftChannelBLE.native.js.map +1 -0
  14. package/dist/react-native/RaftChannelBLE.web.d.ts +40 -0
  15. package/dist/react-native/RaftChannelBLE.web.js +302 -0
  16. package/dist/react-native/RaftChannelBLE.web.js.map +1 -0
  17. package/dist/react-native/RaftChannelBLEFactory.d.ts +10 -0
  18. package/dist/react-native/RaftChannelBLEFactory.js +17 -0
  19. package/dist/react-native/RaftChannelBLEFactory.js.map +1 -0
  20. package/dist/react-native/RaftChannelBLEScanner.native.d.ts +18 -0
  21. package/dist/react-native/RaftChannelBLEScanner.native.js +138 -0
  22. package/dist/react-native/RaftChannelBLEScanner.native.js.map +1 -0
  23. package/dist/react-native/RaftChannelSimulated.d.ts +42 -0
  24. package/dist/react-native/RaftChannelSimulated.js +1000 -0
  25. package/dist/react-native/RaftChannelSimulated.js.map +1 -0
  26. package/dist/react-native/RaftChannelWebSerial.d.ts +39 -0
  27. package/dist/react-native/RaftChannelWebSerial.js +329 -0
  28. package/dist/react-native/RaftChannelWebSerial.js.map +1 -0
  29. package/dist/react-native/RaftChannelWebSocket.d.ts +30 -0
  30. package/dist/react-native/RaftChannelWebSocket.js +222 -0
  31. package/dist/react-native/RaftChannelWebSocket.js.map +1 -0
  32. package/dist/react-native/RaftCommsStats.d.ts +39 -0
  33. package/dist/react-native/RaftCommsStats.js +128 -0
  34. package/dist/react-native/RaftCommsStats.js.map +1 -0
  35. package/dist/react-native/RaftConnEvents.d.ts +39 -0
  36. package/dist/react-native/RaftConnEvents.js +54 -0
  37. package/dist/react-native/RaftConnEvents.js.map +1 -0
  38. package/dist/react-native/RaftConnector.d.ts +248 -0
  39. package/dist/react-native/RaftConnector.js +658 -0
  40. package/dist/react-native/RaftConnector.js.map +1 -0
  41. package/dist/react-native/RaftCustomAttrHandler.d.ts +6 -0
  42. package/dist/react-native/RaftCustomAttrHandler.js +93 -0
  43. package/dist/react-native/RaftCustomAttrHandler.js.map +1 -0
  44. package/dist/react-native/RaftDeviceInfo.d.ts +71 -0
  45. package/dist/react-native/RaftDeviceInfo.js +50 -0
  46. package/dist/react-native/RaftDeviceInfo.js.map +1 -0
  47. package/dist/react-native/RaftDeviceManager.d.ts +61 -0
  48. package/dist/react-native/RaftDeviceManager.js +665 -0
  49. package/dist/react-native/RaftDeviceManager.js.map +1 -0
  50. package/dist/react-native/RaftDeviceMgrIF.d.ts +15 -0
  51. package/dist/react-native/RaftDeviceMgrIF.js +11 -0
  52. package/dist/react-native/RaftDeviceMgrIF.js.map +1 -0
  53. package/dist/react-native/RaftDeviceMsg.d.ts +9 -0
  54. package/dist/react-native/RaftDeviceMsg.js +11 -0
  55. package/dist/react-native/RaftDeviceMsg.js.map +1 -0
  56. package/dist/react-native/RaftDeviceStates.d.ts +37 -0
  57. package/dist/react-native/RaftDeviceStates.js +60 -0
  58. package/dist/react-native/RaftDeviceStates.js.map +1 -0
  59. package/dist/react-native/RaftFileHandler.d.ts +52 -0
  60. package/dist/react-native/RaftFileHandler.js +502 -0
  61. package/dist/react-native/RaftFileHandler.js.map +1 -0
  62. package/dist/react-native/RaftLog.d.ts +22 -0
  63. package/dist/react-native/RaftLog.js +63 -0
  64. package/dist/react-native/RaftLog.js.map +1 -0
  65. package/dist/react-native/RaftMiniHDLC.d.ts +18 -0
  66. package/dist/react-native/RaftMiniHDLC.js +383 -0
  67. package/dist/react-native/RaftMiniHDLC.js.map +1 -0
  68. package/dist/react-native/RaftMsgHandler.d.ts +62 -0
  69. package/dist/react-native/RaftMsgHandler.js +511 -0
  70. package/dist/react-native/RaftMsgHandler.js.map +1 -0
  71. package/dist/react-native/RaftMsgTrackInfo.d.ts +17 -0
  72. package/dist/react-native/RaftMsgTrackInfo.js +42 -0
  73. package/dist/react-native/RaftMsgTrackInfo.js.map +1 -0
  74. package/dist/react-native/RaftProtocolDefs.d.ts +30 -0
  75. package/dist/react-native/RaftProtocolDefs.js +48 -0
  76. package/dist/react-native/RaftProtocolDefs.js.map +1 -0
  77. package/dist/react-native/RaftStreamHandler.d.ts +38 -0
  78. package/dist/react-native/RaftStreamHandler.js +258 -0
  79. package/dist/react-native/RaftStreamHandler.js.map +1 -0
  80. package/dist/react-native/RaftStruct.d.ts +3 -0
  81. package/dist/react-native/RaftStruct.js +258 -0
  82. package/dist/react-native/RaftStruct.js.map +1 -0
  83. package/dist/react-native/RaftSysTypeManager.d.ts +16 -0
  84. package/dist/react-native/RaftSysTypeManager.js +78 -0
  85. package/dist/react-native/RaftSysTypeManager.js.map +1 -0
  86. package/dist/react-native/RaftSystemType.d.ts +30 -0
  87. package/dist/react-native/RaftSystemType.js +3 -0
  88. package/dist/react-native/RaftSystemType.js.map +1 -0
  89. package/dist/react-native/RaftSystemUtils.d.ts +136 -0
  90. package/dist/react-native/RaftSystemUtils.js +412 -0
  91. package/dist/react-native/RaftSystemUtils.js.map +1 -0
  92. package/dist/react-native/RaftTypes.d.ts +195 -0
  93. package/dist/react-native/RaftTypes.js +153 -0
  94. package/dist/react-native/RaftTypes.js.map +1 -0
  95. package/dist/react-native/RaftUpdateEvents.d.ts +33 -0
  96. package/dist/react-native/RaftUpdateEvents.js +46 -0
  97. package/dist/react-native/RaftUpdateEvents.js.map +1 -0
  98. package/dist/react-native/RaftUpdateManager.d.ts +61 -0
  99. package/dist/react-native/RaftUpdateManager.js +621 -0
  100. package/dist/react-native/RaftUpdateManager.js.map +1 -0
  101. package/dist/react-native/RaftUtils.d.ts +128 -0
  102. package/dist/react-native/RaftUtils.js +487 -0
  103. package/dist/react-native/RaftUtils.js.map +1 -0
  104. package/dist/react-native/RaftWifiTypes.d.ts +23 -0
  105. package/dist/react-native/RaftWifiTypes.js +43 -0
  106. package/dist/react-native/RaftWifiTypes.js.map +1 -0
  107. package/dist/react-native/main.d.ts +26 -0
  108. package/dist/react-native/main.js +51 -0
  109. package/dist/react-native/main.js.map +1 -0
  110. package/dist/web/RaftAttributeHandler.js +1 -1
  111. package/dist/web/RaftAttributeHandler.js.map +1 -1
  112. package/dist/web/RaftChannelBLE.web.js +8 -6
  113. package/dist/web/RaftChannelBLE.web.js.map +1 -1
  114. package/dist/web/RaftChannelSimulated.d.ts +10 -0
  115. package/dist/web/RaftChannelSimulated.js +662 -80
  116. package/dist/web/RaftChannelSimulated.js.map +1 -1
  117. package/dist/web/RaftChannelWebSerial.js +2 -2
  118. package/dist/web/RaftChannelWebSerial.js.map +1 -1
  119. package/dist/web/RaftChannelWebSocket.js +16 -1
  120. package/dist/web/RaftChannelWebSocket.js.map +1 -1
  121. package/dist/web/RaftConnector.d.ts +2 -0
  122. package/dist/web/RaftConnector.js +38 -15
  123. package/dist/web/RaftConnector.js.map +1 -1
  124. package/dist/web/RaftCustomAttrHandler.d.ts +2 -0
  125. package/dist/web/RaftCustomAttrHandler.js +54 -26
  126. package/dist/web/RaftCustomAttrHandler.js.map +1 -1
  127. package/dist/web/RaftDeviceInfo.d.ts +3 -1
  128. package/dist/web/RaftDeviceInfo.js +17 -3
  129. package/dist/web/RaftDeviceInfo.js.map +1 -1
  130. package/dist/web/RaftDeviceManager.d.ts +19 -1
  131. package/dist/web/RaftDeviceManager.js +89 -3
  132. package/dist/web/RaftDeviceManager.js.map +1 -1
  133. package/dist/web/RaftDeviceStates.d.ts +1 -1
  134. package/dist/web/RaftDeviceStates.js +2 -2
  135. package/dist/web/RaftDeviceStates.js.map +1 -1
  136. package/dist/web/RaftMsgHandler.js.map +1 -1
  137. package/dist/web/RaftStreamHandler.js +2 -1
  138. package/dist/web/RaftStreamHandler.js.map +1 -1
  139. package/dist/web/RaftStruct.js +197 -147
  140. package/dist/web/RaftStruct.js.map +1 -1
  141. package/dist/web/RaftUpdateManager.js +1 -1
  142. package/dist/web/RaftUpdateManager.js.map +1 -1
  143. package/dist/web/RaftUtils.d.ts +2 -0
  144. package/dist/web/RaftUtils.js +20 -0
  145. package/dist/web/RaftUtils.js.map +1 -1
  146. package/dist/web/main.d.ts +1 -0
  147. package/dist/web/main.js.map +1 -1
  148. package/eslint.config.mjs +33 -0
  149. package/examples/dashboard/package.json +36 -0
  150. package/examples/dashboard/src/CommandPanel.tsx +147 -0
  151. package/examples/dashboard/src/ConnManager.ts +166 -0
  152. package/examples/dashboard/src/DeviceActionsForm.tsx +133 -0
  153. package/examples/dashboard/src/DeviceAttrsForm.tsx +49 -0
  154. package/examples/dashboard/src/DeviceLineChart.tsx +163 -0
  155. package/examples/dashboard/src/DevicePanel.tsx +171 -0
  156. package/examples/dashboard/src/DevicesPanel.tsx +58 -0
  157. package/examples/dashboard/src/DispLedGrid.tsx +110 -0
  158. package/examples/dashboard/src/DispOneLed.tsx +20 -0
  159. package/examples/dashboard/src/LatencyTest.ts +130 -0
  160. package/examples/dashboard/src/LatencyTestPanel.tsx +92 -0
  161. package/examples/dashboard/src/Main.tsx +234 -0
  162. package/examples/dashboard/src/SettingsManager.ts +67 -0
  163. package/examples/dashboard/src/SettingsScreen.tsx +174 -0
  164. package/examples/dashboard/src/StatusPanel.tsx +71 -0
  165. package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +162 -0
  166. package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +91 -0
  167. package/examples/dashboard/src/SystemTypeGeneric/StateInfoGeneric.ts +30 -0
  168. package/examples/dashboard/src/SystemTypeGeneric/SystemTypeGeneric.ts +91 -0
  169. package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
  170. package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
  171. package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
  172. package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
  173. package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
  174. package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
  175. package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
  176. package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
  177. package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +97 -0
  178. package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
  179. package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
  180. package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +119 -0
  181. package/examples/dashboard/src/index.html +15 -0
  182. package/examples/dashboard/src/index.tsx +13 -0
  183. package/examples/dashboard/src/styles.css +408 -0
  184. package/examples/dashboard/tsconfig.json +18 -0
  185. package/jest.config.js +11 -0
  186. package/package.json +4 -7
  187. package/src/RaftAttributeHandler.ts +450 -0
  188. package/src/RaftChannel.ts +32 -0
  189. package/src/RaftChannelBLE.native.ts +617 -0
  190. package/src/RaftChannelBLE.web.ts +374 -0
  191. package/src/RaftChannelBLEFactory.ts +13 -0
  192. package/src/RaftChannelBLEScanner.native.ts +184 -0
  193. package/src/RaftChannelSimulated.ts +1176 -0
  194. package/src/RaftChannelWebSerial.ts +420 -0
  195. package/src/RaftChannelWebSocket.ts +272 -0
  196. package/src/RaftCommsStats.ts +142 -0
  197. package/src/RaftConnEvents.ts +58 -0
  198. package/src/RaftConnector.ts +785 -0
  199. package/src/RaftCustomAttrHandler.ts +117 -0
  200. package/src/RaftDeviceInfo.ts +125 -0
  201. package/src/RaftDeviceManager.ts +844 -0
  202. package/src/RaftDeviceMgrIF.ts +33 -0
  203. package/src/RaftDeviceMsg.ts +20 -0
  204. package/src/RaftDeviceStates.ts +92 -0
  205. package/src/RaftFileHandler.ts +668 -0
  206. package/src/RaftLog.ts +70 -0
  207. package/src/RaftMiniHDLC.ts +396 -0
  208. package/src/RaftMsgHandler.ts +812 -0
  209. package/src/RaftMsgTrackInfo.ts +51 -0
  210. package/src/RaftProtocolDefs.ts +46 -0
  211. package/src/RaftStreamHandler.ts +329 -0
  212. package/src/RaftStruct.ts +282 -0
  213. package/src/RaftSysTypeManager.ts +87 -0
  214. package/src/RaftSystemType.ts +34 -0
  215. package/src/RaftSystemUtils.ts +489 -0
  216. package/src/RaftTypes.ts +279 -0
  217. package/src/RaftUpdateEvents.ts +48 -0
  218. package/src/RaftUpdateManager.ts +781 -0
  219. package/src/RaftUtils.ts +514 -0
  220. package/src/RaftWifiTypes.ts +36 -0
  221. package/src/main.ts +39 -0
  222. package/testdata/TestDeviceTypeRecs.json +492 -0
  223. package/tsconfig.json +30 -0
  224. package/tsconfig.react-native.json +29 -0
@@ -31,6 +31,29 @@ class RaftChannelSimulated {
31
31
  this._requestedFileBlockSize = 500;
32
32
  // Simulated device type information - this is a copy of part of DeviceTypeInfo in RaftCore
33
33
  this._deviceTypeInfo = {
34
+ "AMG8833": {
35
+ "name": "AMG8833",
36
+ "desc": "Thermal Camera",
37
+ "manu": "Panasonic",
38
+ "type": "AMG8833",
39
+ "clas": ["TCAM"],
40
+ "resp": {
41
+ "b": 128,
42
+ "a": [
43
+ {
44
+ "n": "temp",
45
+ "t": "<h[64]",
46
+ "resolution": "8x8",
47
+ "u": "&deg;C",
48
+ "r": [-55, 125],
49
+ "s": -4,
50
+ "d": 64,
51
+ "f": ".2f",
52
+ "o": "float"
53
+ }
54
+ ]
55
+ }
56
+ },
34
57
  "LSM6DS": {
35
58
  "name": "LSM6DS",
36
59
  "desc": "6-Axis IMU",
@@ -96,6 +119,264 @@ class RaftChannelSimulated {
96
119
  }
97
120
  ]
98
121
  }
122
+ },
123
+ "LTR-329": {
124
+ "name": "LTR-329",
125
+ "desc": "Visible light and IR Sensor",
126
+ "manu": "Lite On",
127
+ "type": "LTR-329",
128
+ "clas": ["LGHT"],
129
+ "resp": {
130
+ "b": 4,
131
+ "a": [
132
+ {
133
+ "n": "ir",
134
+ "t": "<h",
135
+ "u": "lux",
136
+ "r": [0, 64000],
137
+ "f": "d",
138
+ "o": "uint16"
139
+ },
140
+ {
141
+ "n": "visible",
142
+ "t": "<h",
143
+ "u": "lux",
144
+ "r": [0, 64000],
145
+ "f": "d",
146
+ "o": "uint16"
147
+ }
148
+ ],
149
+ "c": {
150
+ "n": "ltr329_light_calc",
151
+ "c": "int combined = buf[0] + (((uint16_t)buf[1])<<8); out.ir = buf[2] + (((uint16_t)buf[3])<<8); out.visible = combined - out.ir;",
152
+ "j": "let combined = buf[0] + (buf[1] << 8); let ir = buf[2] + (buf[3] << 8); attrValues['ir'].push(ir); attrValues['visible'].push(Math.max(0, combined - ir));"
153
+ }
154
+ }
155
+ },
156
+ "LTR-390": {
157
+ "name": "LTR-390",
158
+ "desc": "UV and Visible light sensor",
159
+ "manu": "Lite On",
160
+ "type": "LTR-390",
161
+ "clas": ["LGHT"],
162
+ "resp": {
163
+ "b": 5,
164
+ "a": [
165
+ {
166
+ "n": "ambient",
167
+ "t": "<H",
168
+ "u": "lux",
169
+ "r": [0, 64000],
170
+ "d": 1.25,
171
+ "f": ".2f",
172
+ "o": "float"
173
+ },
174
+ {
175
+ "n": "UVI",
176
+ "at": 3,
177
+ "t": "<H",
178
+ "u": "index",
179
+ "r": [0, 64000],
180
+ "d": 24,
181
+ "f": ".3f",
182
+ "o": "float"
183
+ }
184
+ ]
185
+ },
186
+ "actions": []
187
+ },
188
+ "VL53L4CD": {
189
+ "name": "VL53L4CD",
190
+ "desc": "ToF distance sensor with range 0-1.3m",
191
+ "manu": "ST",
192
+ "type": "VL53L4CD",
193
+ "clas": ["DIST"],
194
+ "resp": {
195
+ "b": 3,
196
+ "a": [
197
+ {
198
+ "n": "valid",
199
+ "t": "B",
200
+ "u": "",
201
+ "r": [0, 1],
202
+ "m": "0x04",
203
+ "x": "0x04",
204
+ "s": 2,
205
+ "f": "b",
206
+ "o": "bool",
207
+ "vs": false
208
+ },
209
+ {
210
+ "n": "dist",
211
+ "t": ">H",
212
+ "u": "mm",
213
+ "r": [0, 65535],
214
+ "f": "3d",
215
+ "o": "uint16",
216
+ "vft": "valid"
217
+ }
218
+ ],
219
+ "us": 100000
220
+ }
221
+ },
222
+ "RoboticalServo": {
223
+ "name": "Robotical Servo",
224
+ "desc": "Servo",
225
+ "manu": "Robotical",
226
+ "type": "RoboticalServo",
227
+ "clas": ["SRVO"],
228
+ "resp": {
229
+ "b": 6,
230
+ "a": [
231
+ {
232
+ "n": "angle",
233
+ "t": ">h",
234
+ "r": [-180.0, 180.0],
235
+ "f": ".1f",
236
+ "d": 10,
237
+ "o": "int16",
238
+ "u": "degrees"
239
+ },
240
+ {
241
+ "n": "current",
242
+ "t": "b",
243
+ "r": [-128, 127],
244
+ "f": "d",
245
+ "o": "int8"
246
+ },
247
+ {
248
+ "n": "state",
249
+ "t": "B",
250
+ "r": [0, 255],
251
+ "f": "02x",
252
+ "o": "uint8"
253
+ },
254
+ {
255
+ "n": "velocity",
256
+ "t": ">h",
257
+ "r": [-32768, 32767],
258
+ "f": "d",
259
+ "o": "int16"
260
+ }
261
+ ]
262
+ },
263
+ "actions": [
264
+ {
265
+ "n": "angle",
266
+ "t": ">h",
267
+ "w": "0001",
268
+ "wz": "0064",
269
+ "f": ".1f",
270
+ "mul": 10,
271
+ "sub": 0,
272
+ "r": [-180.0, 180.0],
273
+ "d": 0
274
+ },
275
+ {
276
+ "n": "enable",
277
+ "t": "B",
278
+ "w": "20",
279
+ "f": "b",
280
+ "r": [0, 1],
281
+ "d": 1
282
+ }
283
+ ]
284
+ },
285
+ "RoboticalDCMotor": {
286
+ "name": "Robotical DC Motor",
287
+ "desc": "DCmotor",
288
+ "manu": "Robotical",
289
+ "type": "RoboticalDCMotor",
290
+ "clas": ["MOTR"],
291
+ "resp": {
292
+ "b": 4,
293
+ "a": [
294
+ {
295
+ "n": "angle",
296
+ "t": ">h",
297
+ "r": [-180.0, 180.0],
298
+ "f": ".1f",
299
+ "d": 10,
300
+ "o": "int16",
301
+ "u": "degrees"
302
+ },
303
+ {
304
+ "n": "current",
305
+ "t": "b",
306
+ "r": [-128, 127],
307
+ "f": "d",
308
+ "o": "int8"
309
+ },
310
+ {
311
+ "n": "state",
312
+ "t": "B",
313
+ "r": [0, 255],
314
+ "f": "02x",
315
+ "o": "uint8"
316
+ }
317
+ ]
318
+ },
319
+ "actions": [
320
+ {
321
+ "n": "power",
322
+ "t": ">h",
323
+ "w": "0001",
324
+ "wz": "0064",
325
+ "f": ".1f",
326
+ "mul": 10,
327
+ "sub": 0,
328
+ "r": [-180.0, 180.0],
329
+ "d": 0
330
+ },
331
+ {
332
+ "n": "enable",
333
+ "t": "B",
334
+ "w": "20",
335
+ "f": "b",
336
+ "r": [0, 1],
337
+ "d": 1
338
+ }
339
+ ]
340
+ },
341
+ "RoboticalLEDRing": {
342
+ "name": "Robotical LED Ring",
343
+ "desc": "12 RGB LED Ring",
344
+ "manu": "Robotical",
345
+ "type": "RoboticalLEDRing",
346
+ "clas": ["LED"],
347
+ "resp": {
348
+ "b": 1,
349
+ "a": [
350
+ {
351
+ "n": "status",
352
+ "t": "B",
353
+ "r": [0, 255],
354
+ "f": "02x",
355
+ "o": "uint8"
356
+ }
357
+ ]
358
+ },
359
+ "actions": [
360
+ {
361
+ "n": "pixels",
362
+ "t": "BBBB",
363
+ "w": "04",
364
+ "f": "LEDPIX",
365
+ "NX": 3,
366
+ "NY": 1,
367
+ "concat": false,
368
+ "r": [0, 255]
369
+ },
370
+ {
371
+ "n": "brightness",
372
+ "t": "B",
373
+ "w": "FF01",
374
+ "wz": "&p100",
375
+ "f": "b",
376
+ "r": [0, 255],
377
+ "d": 100
378
+ }
379
+ ]
99
380
  }
100
381
  };
101
382
  }
@@ -139,7 +420,7 @@ class RaftChannelSimulated {
139
420
  }
140
421
  }
141
422
  catch (e) {
142
- RaftLog_1.default.warn(`RaftChannelSimulated.connect - error parsing locator ${locator}`);
423
+ RaftLog_1.default.warn(`RaftChannelSimulated.connect - error parsing locator ${locator}, ${e}`);
143
424
  return false;
144
425
  }
145
426
  }
@@ -303,88 +584,38 @@ class RaftChannelSimulated {
303
584
  // Add 16 bit big endian deviceTimeMs mod 65536 to the buffer
304
585
  dataView.setUint16(bytePos, deviceTimeMs % 65536, false);
305
586
  bytePos += 2;
306
- // Calculate sine wave with phase offsets for each attribute
307
- const numAttributes = attributes.length;
308
- // Adjust frequency based on the device interval with N samples per cycle
309
- const numSamplesPerCycle = 10;
310
- let frequencyHz = 0.1; // Default frequency in Hz
311
- if (deviceIntervalMs > 0) {
312
- frequencyHz = (1000 / deviceIntervalMs) / numSamplesPerCycle;
313
- }
314
- // Amplitude of the sine wave (0 to 1)
315
- const amplitude = 0.8;
316
- // Iterate through attributes and set values
317
- for (let i = 0; i < numAttributes; i++) {
318
- const attr = attributes[i];
319
- // Calculate phase offset for this attribute
320
- const phaseOffset = (2 * Math.PI * i) / numAttributes;
321
- // Generate sine wave value
587
+ const handledByCustomGenerator = this._fillCustomRawData(deviceTypeInfo, dataView, bytePos, dataBlockSizeBytes, deviceIntervalMs, deviceTimeMs);
588
+ if (!handledByCustomGenerator) {
589
+ const numAttributes = attributes.length;
590
+ const numSamplesPerCycle = 10;
591
+ const frequencyHz = (deviceIntervalMs > 0)
592
+ ? (1000 / deviceIntervalMs) / numSamplesPerCycle
593
+ : 0.1;
322
594
  const timeRadians = deviceTimeMs * frequencyHz * (2 * Math.PI) / 1000;
323
- const sinValue = Math.sin(timeRadians + phaseOffset);
324
- // Scale the value to fit within the attribute's range
325
- let scaledValue;
326
- if (attr.r && attr.r.length >= 2) {
327
- const minValue = attr.r[0];
328
- const maxValue = attr.r[1];
329
- const midPoint = (maxValue + minValue) / 2;
330
- const range = (maxValue - minValue) / 2;
331
- scaledValue = midPoint + sinValue * range * amplitude;
332
- }
333
- else {
334
- // Default range if not specified
335
- scaledValue = sinValue * 1000 * amplitude;
336
- }
337
- // Convert to raw integer value if needed
338
- let rawValue = scaledValue;
339
- if (attr.d) {
340
- // Multiply by the divisor to get the raw value (reverse of what happens when decoding)
341
- rawValue = scaledValue * attr.d;
342
- }
343
- // Write the value to the buffer based on its type
344
- if (attr.t === "b") {
345
- dataView.setUint8(bytePos, Math.round(rawValue));
346
- bytePos += 1;
347
- }
348
- else if (attr.t === "B") {
349
- dataView.setUint8(bytePos, Math.round(rawValue));
350
- bytePos += 1;
351
- }
352
- else if (attr.t === "c") {
353
- dataView.setInt8(bytePos, Math.round(rawValue));
354
- bytePos += 1;
355
- }
356
- else if (attr.t === "C") {
357
- dataView.setUint8(bytePos, Math.round(rawValue));
358
- bytePos += 1;
359
- }
360
- else if (attr.t === "<h") {
361
- dataView.setInt16(bytePos, Math.round(rawValue), true); // Little endian
362
- bytePos += 2;
363
- }
364
- else if (attr.t === ">h") {
365
- dataView.setInt16(bytePos, Math.round(rawValue), false); // Big endian
366
- bytePos += 2;
367
- }
368
- else if (attr.t === "<H") {
369
- dataView.setUint16(bytePos, Math.round(rawValue), true); // Little endian
370
- bytePos += 2;
371
- }
372
- else if (attr.t === ">H") {
373
- dataView.setUint16(bytePos, Math.round(rawValue), false); // Big endian
374
- bytePos += 2;
375
- }
376
- else if (attr.t === "f" || attr.t === "<f") {
377
- dataView.setFloat32(bytePos, rawValue, true); // Little endian
378
- bytePos += 4;
379
- }
380
- else if (attr.t === ">f") {
381
- dataView.setFloat32(bytePos, rawValue, false); // Big endian
382
- bytePos += 4;
383
- }
384
- else {
385
- RaftLog_1.default.warn(`RaftChannelSimulated._createSimulatedDeviceInfoMsg - unsupported attribute type ${attr.t}`);
595
+ // Iterate through attributes and fill the payload
596
+ for (let attrIdx = 0; attrIdx < numAttributes; attrIdx++) {
597
+ const attr = attributes[attrIdx];
598
+ const { typeCode, repeatCount, littleEndian } = this._parseAttrType(attr.t);
599
+ const scaledValues = this._generateAttributeScaledValues(attr, attrIdx, repeatCount, numAttributes, timeRadians, deviceTimeMs);
600
+ if (scaledValues.length !== repeatCount) {
601
+ RaftLog_1.default.warn(`RaftChannelSimulated._createSimulatedDeviceInfoMsg - value count mismatch for ${attr.n}`);
602
+ continue;
603
+ }
604
+ for (let elemIdx = 0; elemIdx < repeatCount; elemIdx++) {
605
+ const scaledValue = scaledValues[elemIdx];
606
+ const rawValue = this._prepareRawValue(attr, typeCode, scaledValue);
607
+ const nextBytePos = this._writeRawValueToBuffer(dataView, bytePos, typeCode, littleEndian, rawValue);
608
+ if (nextBytePos < 0) {
609
+ RaftLog_1.default.warn(`RaftChannelSimulated._createSimulatedDeviceInfoMsg - buffer overflow writing ${attr.n}`);
610
+ break;
611
+ }
612
+ bytePos = nextBytePos;
613
+ }
386
614
  }
387
615
  }
616
+ else {
617
+ bytePos += dataBlockSizeBytes;
618
+ }
388
619
  // Convert the buffer to a byte array
389
620
  const dataBytes = new Uint8Array(dataBuffer);
390
621
  // Create the JSON message structure
@@ -407,6 +638,357 @@ class RaftChannelSimulated {
407
638
  msgPrefixBytes.set(encodedMsg, 2);
408
639
  return msgPrefixBytes;
409
640
  }
641
+ _parseAttrType(attrType) {
642
+ const repeatMatch = attrType.match(/\[(\d+)\]\s*$/);
643
+ const repeatCount = repeatMatch ? parseInt(repeatMatch[1], 10) : 1;
644
+ const coreType = repeatMatch ? attrType.slice(0, repeatMatch.index) : attrType;
645
+ let littleEndian = false;
646
+ let typeCode = coreType.trim();
647
+ if (typeCode.startsWith("<")) {
648
+ littleEndian = true;
649
+ typeCode = typeCode.slice(1);
650
+ }
651
+ else if (typeCode.startsWith(">")) {
652
+ littleEndian = false;
653
+ typeCode = typeCode.slice(1);
654
+ }
655
+ else if (typeCode === "f") {
656
+ // Match previous behaviour - plain "f" treated as little endian floats
657
+ littleEndian = true;
658
+ }
659
+ return { typeCode, repeatCount, littleEndian };
660
+ }
661
+ _generateAttributeScaledValues(attr, attrIdx, repeatCount, numAttributes, timeRadians, deviceTimeMs) {
662
+ const amplitude = 0.8;
663
+ if (repeatCount > 1) {
664
+ const useThermalGrid = (attr && typeof attr.resolution === "string") || repeatCount >= 16;
665
+ if (useThermalGrid) {
666
+ return this._generateThermalGridValues(attr, repeatCount, timeRadians, deviceTimeMs);
667
+ }
668
+ const values = [];
669
+ for (let elemIdx = 0; elemIdx < repeatCount; elemIdx++) {
670
+ const phaseOffset = (2 * Math.PI * (attrIdx + elemIdx / repeatCount)) / Math.max(1, numAttributes);
671
+ const sinValue = Math.sin(timeRadians + phaseOffset);
672
+ if (Array.isArray(attr.r) && attr.r.length >= 2) {
673
+ const minValue = attr.r[0];
674
+ const maxValue = attr.r[1];
675
+ const midPoint = (maxValue + minValue) / 2;
676
+ const range = (maxValue - minValue) / 2;
677
+ const value = midPoint + sinValue * range * amplitude;
678
+ values.push(Math.min(maxValue, Math.max(minValue, value)));
679
+ }
680
+ else {
681
+ values.push(sinValue * 1000 * amplitude);
682
+ }
683
+ }
684
+ return values;
685
+ }
686
+ const phaseOffset = numAttributes > 0 ? (2 * Math.PI * attrIdx) / numAttributes : 0;
687
+ const sinValue = Math.sin(timeRadians + phaseOffset);
688
+ if (Array.isArray(attr.r) && attr.r.length >= 2) {
689
+ const minValue = attr.r[0];
690
+ const maxValue = attr.r[1];
691
+ const midPoint = (maxValue + minValue) / 2;
692
+ const range = (maxValue - minValue) / 2;
693
+ const value = midPoint + sinValue * range * amplitude;
694
+ return [Math.min(maxValue, Math.max(minValue, value))];
695
+ }
696
+ return [sinValue * 1000 * amplitude];
697
+ }
698
+ _generateThermalGridValues(attr, repeatCount, timeRadians, deviceTimeMs) {
699
+ const { rows, cols } = this._getGridDimensions(attr, repeatCount);
700
+ const values = [];
701
+ const ambientBase = 24 + 2 * Math.sin(deviceTimeMs / 7000);
702
+ const hotspotPhase = deviceTimeMs / 3200;
703
+ const hotspotRow = (Math.sin(hotspotPhase) + 1) * (rows - 1) / 2;
704
+ const hotspotCol = (Math.cos(hotspotPhase) + 1) * (cols - 1) / 2;
705
+ const hotspotAmplitude = 6;
706
+ const sigma = Math.max(rows, cols) / 3 || 1;
707
+ for (let idx = 0; idx < repeatCount; idx++) {
708
+ const row = Math.floor(idx / cols);
709
+ const col = idx % cols;
710
+ const dist = Math.hypot(row - hotspotRow, col - hotspotCol);
711
+ const hotspot = hotspotAmplitude * Math.exp(-(dist * dist) / (2 * sigma * sigma));
712
+ const gentleWave = 0.5 * Math.sin(timeRadians + row * 0.35 + col * 0.25);
713
+ let value = ambientBase + hotspot + gentleWave;
714
+ if (Array.isArray(attr.r) && attr.r.length >= 2) {
715
+ value = Math.min(attr.r[1], Math.max(attr.r[0], value));
716
+ }
717
+ values.push(value);
718
+ }
719
+ return values;
720
+ }
721
+ _fillCustomRawData(deviceTypeInfo, dataView, bytePos, dataBlockSizeBytes, deviceIntervalMs, deviceTimeMs) {
722
+ var _a, _b, _c, _d, _e, _f;
723
+ switch (deviceTypeInfo.type) {
724
+ case "LTR-329": {
725
+ if (dataBlockSizeBytes < 4) {
726
+ return false;
727
+ }
728
+ const frequencyHz = (deviceIntervalMs > 0)
729
+ ? (1000 / deviceIntervalMs) / 10
730
+ : 0.1;
731
+ const timeRadians = deviceTimeMs * frequencyHz * (2 * Math.PI) / 1000;
732
+ const range = (_d = (_c = (_b = (_a = deviceTypeInfo.resp) === null || _a === void 0 ? void 0 : _a.a) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.r) !== null && _d !== void 0 ? _d : [0, 64000];
733
+ const minLux = (_e = range[0]) !== null && _e !== void 0 ? _e : 0;
734
+ const maxLux = (_f = range[1]) !== null && _f !== void 0 ? _f : 64000;
735
+ const baseLux = (maxLux + minLux) / 4;
736
+ const amplitudeLux = (maxLux - minLux) / 6;
737
+ let combined = Math.round(baseLux + amplitudeLux * Math.sin(timeRadians));
738
+ combined = Math.max(minLux, Math.min(maxLux, combined));
739
+ const irBase = combined * 0.35;
740
+ const irVariance = (combined * 0.15) * Math.sin(timeRadians + Math.PI / 4);
741
+ let ir = Math.round(irBase + irVariance);
742
+ ir = Math.max(minLux, Math.min(combined, ir));
743
+ dataView.setUint16(bytePos, combined, true);
744
+ dataView.setUint16(bytePos + 2, ir, true);
745
+ return true;
746
+ }
747
+ case "LTR-390": {
748
+ if (dataBlockSizeBytes < 5) {
749
+ return false;
750
+ }
751
+ const numerator = (deviceIntervalMs > 0) ? (1000 / deviceIntervalMs) : 0;
752
+ const timeRadians = deviceTimeMs * numerator * (2 * Math.PI) / 1000 / 10;
753
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
754
+ const ambientLux = clamp(8000 + 4500 * Math.sin(timeRadians), 0, 64000);
755
+ const ambientRaw = clamp(Math.round(ambientLux * 1.25), 0, 0xFFFFF);
756
+ dataView.setUint8(bytePos, ambientRaw & 0xFF);
757
+ dataView.setUint8(bytePos + 1, (ambientRaw >> 8) & 0xFF);
758
+ dataView.setUint8(bytePos + 2, (ambientRaw >> 16) & 0x0F);
759
+ const uviIndex = clamp(3.5 + 1.8 * Math.sin(timeRadians + Math.PI / 3), 0, 12);
760
+ const uviRaw = clamp(Math.round(uviIndex * 24), 0, 0xFFFF);
761
+ dataView.setUint16(bytePos + 3, uviRaw, true);
762
+ return true;
763
+ }
764
+ case "VL53L4CD": {
765
+ if (dataBlockSizeBytes < 3) {
766
+ return false;
767
+ }
768
+ const rangeMinMm = 40;
769
+ const rangeMaxMm = 1300;
770
+ const cycleMs = 9000;
771
+ const timeInCycle = deviceTimeMs % cycleMs;
772
+ // Simulate periods where the sensor reports invalid readings (target lost)
773
+ const invalidReading = timeInCycle < 600 ||
774
+ (timeInCycle > 4200 && timeInCycle < 5100) ||
775
+ (timeInCycle > 7800);
776
+ const isValid = !invalidReading;
777
+ // Generate a smooth oscillating distance while valid
778
+ const phase = (deviceTimeMs % cycleMs) / cycleMs;
779
+ const smoothWave = 0.5 * (1 - Math.cos(phase * 2 * Math.PI));
780
+ const slowDrift = 0.05 * Math.sin(deviceTimeMs / 5000);
781
+ const fastRipple = 0.02 * Math.sin(deviceTimeMs / 200);
782
+ const normalized = Math.min(1, Math.max(0, smoothWave + slowDrift + fastRipple));
783
+ let distanceMm = Math.round(rangeMinMm + normalized * (rangeMaxMm - rangeMinMm));
784
+ // Clamp to the reported range
785
+ distanceMm = Math.max(rangeMinMm, Math.min(rangeMaxMm, distanceMm));
786
+ if (!isValid) {
787
+ distanceMm = 0;
788
+ }
789
+ const statusRaw = isValid ? 0x00 : 0x04;
790
+ dataView.setUint8(bytePos, statusRaw);
791
+ dataView.setUint16(bytePos + 1, distanceMm, false);
792
+ return true;
793
+ }
794
+ case "RoboticalLEDRing": {
795
+ if (dataBlockSizeBytes < 1) {
796
+ return false;
797
+ }
798
+ const animationPhase = (deviceTimeMs % 6000) / 6000;
799
+ const patternIndex = Math.floor(animationPhase * 6) % 6;
800
+ const brightness = Math.round((Math.sin(deviceTimeMs / 600) + 1) * 127.5);
801
+ const isAnimating = Math.sin(deviceTimeMs / 1500) > 0;
802
+ const status = ((patternIndex & 0x07) << 4) | Math.min(0x0F, brightness >> 4) | (isAnimating ? 0x80 : 0x00);
803
+ dataView.setUint8(bytePos, status & 0xFF);
804
+ return true;
805
+ }
806
+ case "RoboticalDCMotor": {
807
+ if (dataBlockSizeBytes < 4) {
808
+ return false;
809
+ }
810
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
811
+ const cycleMs = 3200;
812
+ const cycleIndex = Math.floor(deviceTimeMs / cycleMs);
813
+ const direction = (cycleIndex % 2 === 0) ? 1 : -1;
814
+ const progress = (deviceTimeMs % cycleMs) / cycleMs;
815
+ const baseAngle = (progress * 360) - 180;
816
+ const angleDegrees = direction > 0 ? baseAngle : -baseAngle;
817
+ const angleRaw = clamp(Math.round(angleDegrees * 10), -1800, 1800);
818
+ const ripple = 6 * Math.sin((deviceTimeMs % cycleMs) / 180);
819
+ const loadComponent = Math.abs(baseAngle) * 0.12;
820
+ const currentRaw = clamp(Math.round(24 + loadComponent + ripple), -128, 127);
821
+ const highLoad = Math.abs(loadComponent) > 8;
822
+ const directionBit = direction > 0 ? 0x01 : 0x00;
823
+ const stateRaw = 0x02 | directionBit | (highLoad ? 0x04 : 0x00);
824
+ dataView.setInt16(bytePos, angleRaw, false);
825
+ dataView.setInt8(bytePos + 2, currentRaw);
826
+ dataView.setUint8(bytePos + 3, stateRaw);
827
+ return true;
828
+ }
829
+ case "RoboticalServo": {
830
+ if (dataBlockSizeBytes < 6) {
831
+ return false;
832
+ }
833
+ const swingAmplitudeDeg = 90;
834
+ const cycleMs = 4000;
835
+ const angularSpeedRadPerMs = (2 * Math.PI) / cycleMs;
836
+ const phaseRadians = (deviceTimeMs % cycleMs) * angularSpeedRadPerMs;
837
+ const angleDegrees = swingAmplitudeDeg * Math.sin(phaseRadians);
838
+ const velocityDegPerSec = swingAmplitudeDeg * angularSpeedRadPerMs * 1000 * Math.cos(phaseRadians);
839
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
840
+ const angleRaw = clamp(Math.round(angleDegrees * 10), -1800, 1800);
841
+ let velocityRaw = Math.round(velocityDegPerSec);
842
+ velocityRaw = clamp(velocityRaw, -32768, 32767);
843
+ let currentRaw = Math.round(20 + 0.08 * Math.abs(velocityDegPerSec));
844
+ currentRaw = clamp(currentRaw, 0, 127);
845
+ const isMoving = Math.abs(velocityDegPerSec) > 5;
846
+ const stateRaw = (0x02) | (isMoving ? 0x01 : 0);
847
+ dataView.setInt16(bytePos, angleRaw, false);
848
+ dataView.setInt8(bytePos + 2, currentRaw);
849
+ dataView.setUint8(bytePos + 3, stateRaw);
850
+ dataView.setInt16(bytePos + 4, velocityRaw, false);
851
+ return true;
852
+ }
853
+ default:
854
+ return false;
855
+ }
856
+ }
857
+ _getGridDimensions(attr, repeatCount) {
858
+ if (attr && typeof attr.resolution === "string") {
859
+ const match = attr.resolution.match(/(\d+)\s*x\s*(\d+)/i);
860
+ if (match) {
861
+ const rows = parseInt(match[1], 10);
862
+ const cols = parseInt(match[2], 10);
863
+ if (rows > 0 && cols > 0) {
864
+ return { rows, cols };
865
+ }
866
+ }
867
+ }
868
+ const side = Math.round(Math.sqrt(repeatCount));
869
+ if (side > 0 && side * side === repeatCount) {
870
+ return { rows: side, cols: side };
871
+ }
872
+ return { rows: repeatCount, cols: 1 };
873
+ }
874
+ _prepareRawValue(attr, typeCode, scaledValue) {
875
+ if (this._isFloatType(typeCode)) {
876
+ return scaledValue;
877
+ }
878
+ let raw = scaledValue;
879
+ if (attr && typeof attr.a === "number") {
880
+ raw -= attr.a;
881
+ }
882
+ if (attr && typeof attr.d === "number") {
883
+ raw *= attr.d;
884
+ }
885
+ if (attr && typeof attr.s === "number" && attr.s !== 0) {
886
+ const shift = attr.s;
887
+ const shiftFactor = Math.pow(2, Math.abs(shift));
888
+ if (shift > 0) {
889
+ raw *= shiftFactor;
890
+ }
891
+ else {
892
+ raw /= shiftFactor;
893
+ }
894
+ }
895
+ return Math.round(raw);
896
+ }
897
+ _writeRawValueToBuffer(dataView, bytePos, typeCode, littleEndian, rawValue) {
898
+ const valueSize = this._byteSizeForType(typeCode);
899
+ if (valueSize <= 0 || bytePos + valueSize > dataView.byteLength) {
900
+ return -1;
901
+ }
902
+ switch (typeCode) {
903
+ case "b":
904
+ dataView.setInt8(bytePos, this._clampRawValue(rawValue, typeCode));
905
+ break;
906
+ case "c":
907
+ dataView.setInt8(bytePos, this._clampRawValue(rawValue, typeCode));
908
+ break;
909
+ case "B":
910
+ case "C":
911
+ dataView.setUint8(bytePos, this._clampRawValue(rawValue, typeCode));
912
+ break;
913
+ case "?":
914
+ dataView.setUint8(bytePos, rawValue ? 1 : 0);
915
+ break;
916
+ case "h":
917
+ dataView.setInt16(bytePos, this._clampRawValue(rawValue, typeCode), littleEndian);
918
+ break;
919
+ case "H":
920
+ dataView.setUint16(bytePos, this._clampRawValue(rawValue, typeCode), littleEndian);
921
+ break;
922
+ case "i":
923
+ case "l":
924
+ dataView.setInt32(bytePos, this._clampRawValue(rawValue, typeCode), littleEndian);
925
+ break;
926
+ case "I":
927
+ case "L":
928
+ dataView.setUint32(bytePos, this._clampRawValue(rawValue, typeCode), littleEndian);
929
+ break;
930
+ case "f":
931
+ dataView.setFloat32(bytePos, rawValue, littleEndian);
932
+ break;
933
+ case "d":
934
+ dataView.setFloat64(bytePos, rawValue, littleEndian);
935
+ break;
936
+ default:
937
+ RaftLog_1.default.warn(`RaftChannelSimulated._writeRawValueToBuffer - unsupported attribute type ${typeCode}`);
938
+ return -1;
939
+ }
940
+ return bytePos + valueSize;
941
+ }
942
+ _byteSizeForType(typeCode) {
943
+ switch (typeCode) {
944
+ case "b":
945
+ case "B":
946
+ case "c":
947
+ case "C":
948
+ case "?":
949
+ return 1;
950
+ case "h":
951
+ case "H":
952
+ return 2;
953
+ case "i":
954
+ case "I":
955
+ case "l":
956
+ case "L":
957
+ case "f":
958
+ return 4;
959
+ case "d":
960
+ return 8;
961
+ default:
962
+ return 0;
963
+ }
964
+ }
965
+ _clampRawValue(rawValue, typeCode) {
966
+ const value = Math.round(rawValue);
967
+ switch (typeCode) {
968
+ case "b":
969
+ case "c":
970
+ return Math.max(-128, Math.min(127, value));
971
+ case "B":
972
+ case "C":
973
+ case "?":
974
+ return Math.max(0, Math.min(255, value));
975
+ case "h":
976
+ return Math.max(-32768, Math.min(32767, value));
977
+ case "H":
978
+ return Math.max(0, Math.min(65535, value));
979
+ case "i":
980
+ case "l":
981
+ return Math.max(-2147483648, Math.min(2147483647, value));
982
+ case "I":
983
+ case "L":
984
+ return Math.max(0, Math.min(4294967295, value));
985
+ default:
986
+ return value;
987
+ }
988
+ }
989
+ _isFloatType(typeCode) {
990
+ return typeCode === "f" || typeCode === "d";
991
+ }
410
992
  // Helper function to convert bytes to hex string
411
993
  _bytesToHexStr(bytes) {
412
994
  return Array.from(bytes)