Created
May 30, 2024 18:06
-
-
Save stefanbraun-private/79b7b58aefa647a7805fc86aa994b68e to your computer and use it in GitHub Desktop.
payload decoder function for "Helium Mappers API" with "SenseCAP T1000-A" on "Helium Foundation Console"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Decoder Script from https://wiki.seeedstudio.com/SenseCAP_Decoder/ | |
| // reformated decoded object for Helium Mappers API | |
| // | |
| // BrS, 29.05.2024 | |
| function Decoder(bytes, port, uplink_info) { | |
| /* | |
| The uplink_info variable is an OPTIONAL third parameter that provides the following: | |
| uplink_info = { | |
| type: "join", | |
| uuid: <UUIDv4>, | |
| id: <device id>, | |
| name: <device name>, | |
| dev_eui: <dev_eui>, | |
| app_eui: <app_eui>, | |
| metadata: {...}, | |
| fcnt: <integer>, | |
| reported_at: <timestamp>, | |
| port: <integer>, | |
| devaddr: <devaddr>, | |
| hotspots: {...}, | |
| hold_time: <integer> | |
| } | |
| */ | |
| if (uplink_info) { | |
| // do something with uplink_info fields | |
| } | |
| const bytesString = bytes2HexString(bytes) | |
| const originMessage = bytesString.toLocaleUpperCase() | |
| const fport = parseInt(port) | |
| const decoded = { | |
| valid: true, | |
| err: 0, | |
| payload: bytesString, | |
| messages: [], | |
| // BrS, 29.05.2024: adding location data into structure of decoded payload (for Helium Mapping) | |
| latitude: null, | |
| longitude: null, | |
| altitude: 0, | |
| accuracy: 12, | |
| } | |
| if (fport === 199 || fport === 192) { | |
| decoded.messages.push({fport: fport, payload: bytesString}) | |
| return decoded | |
| } | |
| if (fport !== 5) { | |
| decoded.valid = false | |
| return decoded | |
| } | |
| let measurement = messageAnalyzed(originMessage) | |
| if (measurement.length === 0) { | |
| decoded.valid = false | |
| return decoded | |
| } | |
| for (let message of measurement) { | |
| if (message.length === 0) { | |
| continue | |
| } | |
| let elements = [] | |
| for (let element of message) { | |
| if (element.errorCode) { | |
| decoded.err = element.errorCode | |
| decoded.errMessage = element.error | |
| } else { | |
| elements.push(element) | |
| // BrS, 29.05.2024: adding position to fixed position of decoded payload (for Helium Mapping) | |
| if (element.type == 'Longitude'){ | |
| decoded.longitude = element.measurementValue | |
| } | |
| if (element.type == 'Latitude'){ | |
| decoded.latitude = element.measurementValue | |
| } | |
| } | |
| } | |
| if (elements.length > 0) { | |
| decoded.messages.push(elements) | |
| } | |
| } | |
| // decoded.messages = measurement | |
| return decoded | |
| } | |
| function messageAnalyzed (messageValue) { | |
| try { | |
| let frames = unpack(messageValue) | |
| let measurementResultArray = [] | |
| for (let i = 0; i < frames.length; i++) { | |
| let item = frames[i] | |
| let dataId = item.dataId | |
| let dataValue = item.dataValue | |
| let measurementArray = deserialize(dataId, dataValue) | |
| measurementResultArray.push(measurementArray) | |
| } | |
| return measurementResultArray | |
| } catch (e) { | |
| return e.toString() | |
| } | |
| } | |
| function unpack (messageValue) { | |
| let frameArray = [] | |
| for (let i = 0; i < messageValue.length; i++) { | |
| let remainMessage = messageValue | |
| let dataId = remainMessage.substring(0, 2).toUpperCase() | |
| let dataValue | |
| let dataObj = {} | |
| let packageLen | |
| switch (dataId) { | |
| case '01': | |
| packageLen = 94 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '02': | |
| packageLen = 32 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '03': | |
| packageLen = 64 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '04': | |
| packageLen = 20 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '05': | |
| packageLen = 10 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '06': | |
| packageLen = 44 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '07': | |
| packageLen = 84 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '08': | |
| packageLen = 70 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '09': | |
| packageLen = 36 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '0A': | |
| packageLen = 76 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '0B': | |
| packageLen = 62 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '0C': | |
| packageLen = 2 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| break | |
| case '0D': | |
| packageLen = 10 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '0E': | |
| packageLen = getInt(remainMessage.substring(8, 10)) * 2 + 10 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, 8) + remainMessage.substring(10, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '0F': | |
| packageLen = 34 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '10': | |
| packageLen = 26 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| case '11': | |
| packageLen = 28 | |
| if (remainMessage.length < packageLen) { | |
| return frameArray | |
| } | |
| dataValue = remainMessage.substring(2, packageLen) | |
| messageValue = remainMessage.substring(packageLen) | |
| dataObj = { | |
| 'dataId': dataId, 'dataValue': dataValue | |
| } | |
| break | |
| default: | |
| return frameArray | |
| } | |
| if (dataValue.length < 2) { | |
| break | |
| } | |
| frameArray.push(dataObj) | |
| } | |
| return frameArray | |
| } | |
| function deserialize (dataId, dataValue) { | |
| let measurementArray = [] | |
| let eventList = [] | |
| let measurement = {} | |
| let collectTime = 0 | |
| let groupId = 0 | |
| let shardFlag = {} | |
| let payload = '' | |
| let result = [] | |
| let dataArr = [] | |
| switch (dataId) { | |
| case '01': | |
| measurementArray = getUpShortInfo(dataValue) | |
| measurementArray.push(...getMotionSetting(dataValue.substring(30, 40))) | |
| measurementArray.push(...getStaticSetting(dataValue.substring(40, 46))) | |
| measurementArray.push(...getShockSetting(dataValue.substring(46, 52))) | |
| measurementArray.push(...getTempSetting(dataValue.substring(52, 72))) | |
| measurementArray.push(...getLightSetting(dataValue.substring(72, 92))) | |
| break | |
| case '02': | |
| measurementArray = getUpShortInfo(dataValue) | |
| break | |
| case '03': | |
| measurementArray.push(...getMotionSetting(dataValue.substring(0, 10))) | |
| measurementArray.push(...getStaticSetting(dataValue.substring(10, 16))) | |
| measurementArray.push(...getShockSetting(dataValue.substring(16, 22))) | |
| measurementArray.push(...getTempSetting(dataValue.substring(22, 42))) | |
| measurementArray.push(...getLightSetting(dataValue.substring(42, 62))) | |
| break | |
| case '04': | |
| let interval = 0 | |
| let workMode = getInt(dataValue.substring(0, 2)) | |
| let heartbeatInterval = getMinsByMin(dataValue.substring(4, 8)) | |
| let periodicInterval = getMinsByMin(dataValue.substring(8, 12)) | |
| let eventInterval = getMinsByMin(dataValue.substring(12, 16)) | |
| switch (workMode) { | |
| case 0: | |
| interval = heartbeatInterval | |
| break | |
| case 1: | |
| interval = periodicInterval | |
| break | |
| case 2: | |
| interval = eventInterval | |
| break | |
| } | |
| measurementArray = [ | |
| {measurementId: '3940', type: 'Work Mode', measurementValue: workMode}, | |
| {measurementId: '3942', type: 'Heartbeat Interval', measurementValue: heartbeatInterval}, | |
| {measurementId: '3943', type: 'Periodic Interval', measurementValue: periodicInterval}, | |
| {measurementId: '3944', type: 'Event Interval', measurementValue: eventInterval}, | |
| {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(16, 18))}, | |
| {measurementId: '3900', type: 'Uplink Interval', measurementValue: interval} | |
| ] | |
| break; | |
| case '05': | |
| measurementArray = [ | |
| {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(0, 2))}, | |
| {measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(dataValue.substring(2, 4))}, | |
| {measurementId: '3965', type: 'Positioning Strategy', measurementValue: getPositioningStrategy(dataValue.substring(4, 6))}, | |
| {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(6, 8))} | |
| ] | |
| break | |
| case '06': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, | |
| {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, | |
| {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, | |
| {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(40, 42))} | |
| ] | |
| break | |
| case '07': | |
| eventList = getEventStatus(dataValue.substring(0, 6)) | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, | |
| {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, | |
| {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(80, 82))} | |
| ] | |
| break | |
| case '08': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, | |
| {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, | |
| {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(66, 68))} | |
| ] | |
| break | |
| case '09': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, | |
| {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(32, 34))} | |
| ] | |
| break | |
| case '0A': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(72, 74))} | |
| ] | |
| break | |
| case '0B': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray = [ | |
| {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, | |
| {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, | |
| {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(58, 60))}, | |
| ] | |
| break | |
| case '0D': | |
| let errorCode = getInt(dataValue) | |
| let error = '' | |
| switch (errorCode) { | |
| case 1: | |
| error = 'FAILED TO OBTAIN THE UTC TIMESTAMP' | |
| break | |
| case 2: | |
| error = 'ALMANAC TOO OLD' | |
| break | |
| case 3: | |
| error = 'DOPPLER ERROR' | |
| break | |
| } | |
| measurementArray.push({errorCode, error}) | |
| break | |
| case '0E': | |
| shardFlag = getShardFlag(dataValue.substring(0, 2)) | |
| groupId = getInt(dataValue.substring(2, 6)) | |
| payload = dataValue.substring(6) | |
| measurement = { | |
| measurementId: '6152', | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'gnss-ng payload', | |
| measurementValue: payload | |
| } | |
| measurementArray.push(measurement) | |
| break | |
| case '0F': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| shardFlag = getShardFlag(dataValue.substring(26, 28)) | |
| groupId = getInt(dataValue.substring(28, 32)) | |
| measurementArray.push({ | |
| measurementId: '4200', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Event Status', | |
| measurementValue: getEventStatus(dataValue.substring(0, 6)) | |
| }) | |
| measurementArray.push({ | |
| measurementId: '4097', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Air Temperature', | |
| measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) | |
| }) | |
| measurementArray.push({ | |
| measurementId: '4199', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Light', | |
| measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) | |
| }) | |
| measurementArray.push({ | |
| measurementId: '3000', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Battery', | |
| measurementValue: '' + getBattery(dataValue.substring(24, 26)) | |
| }) | |
| break | |
| case '10': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| shardFlag = getShardFlag(dataValue.substring(18, 20)) | |
| groupId = getInt(dataValue.substring(20, 24)) | |
| measurementArray.push({ | |
| measurementId: '4200', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Event Status', | |
| measurementValue: getEventStatus(dataValue.substring(0, 6)) | |
| }) | |
| measurementArray.push({ | |
| measurementId: '3000', | |
| timestamp: collectTime, | |
| motionId: getMotionId(dataValue.substring(6, 8)), | |
| groupId: groupId, | |
| index: shardFlag.index, | |
| count: shardFlag.count, | |
| type: 'Battery', | |
| measurementValue: '' + getBattery(dataValue.substring(16, 18)) | |
| }) | |
| break | |
| case '11': | |
| collectTime = getUTCTimestamp(dataValue.substring(8, 16)) | |
| measurementArray.push({ | |
| measurementId: '3576', | |
| timestamp: collectTime, | |
| type: 'Positing Status', | |
| measurementValue: '' + getPositingStatus(dataValue.substring(0, 2)) | |
| }) | |
| measurementArray.push({ | |
| timestamp: collectTime, | |
| measurementId: '4200', | |
| type: 'Event Status', | |
| measurementValue: getEventStatus(dataValue.substring(2, 8)) | |
| }) | |
| if (!isNaN(parseFloat(getSensorValue(dataValue.substring(16, 20), 10)))) { | |
| measurementArray.push({ | |
| timestamp: collectTime, | |
| measurementId: '4097', | |
| type: 'Air Temperature', | |
| measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) | |
| }) | |
| } | |
| if (!isNaN(parseFloat(getSensorValue(dataValue.substring(20, 24))))) { | |
| measurementArray.push({ | |
| timestamp: collectTime, | |
| measurementId: '4199', | |
| type: 'Light', | |
| measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) | |
| }) | |
| } | |
| measurementArray.push({ | |
| timestamp: collectTime, | |
| measurementId: '3000', | |
| type: 'Battery', | |
| measurementValue: '' + getBattery(dataValue.substring(24, 26)) | |
| }) | |
| break | |
| } | |
| return measurementArray | |
| } | |
| function getMotionId (str) { | |
| return getInt(str) | |
| } | |
| function getPositingStatus (str) { | |
| return getInt(str) | |
| } | |
| function getUpShortInfo (messageValue) { | |
| return [ | |
| { | |
| measurementId: '3000', type: 'Battery', measurementValue: getBattery(messageValue.substring(0, 2)) | |
| }, { | |
| measurementId: '3502', type: 'Firmware Version', measurementValue: getSoftVersion(messageValue.substring(2, 6)) | |
| }, { | |
| measurementId: '3001', type: 'Hardware Version', measurementValue: getHardVersion(messageValue.substring(6, 10)) | |
| }, { | |
| measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(messageValue.substring(10, 12)) | |
| }, { | |
| measurementId: '3965', type: 'Positioning Strategy', measurementValue: getPositioningStrategy(messageValue.substring(12, 14)) | |
| }, { | |
| measurementId: '3942', type: 'Heartbeat Interval', measurementValue: getMinsByMin(messageValue.substring(14, 18)) | |
| }, { | |
| measurementId: '3943', type: 'Periodic Interval', measurementValue: getMinsByMin(messageValue.substring(18, 22)) | |
| }, { | |
| measurementId: '3944', type: 'Event Interval', measurementValue: getMinsByMin(messageValue.substring(22, 26)) | |
| }, { | |
| measurementId: '3945', type: 'Sensor Enable', measurementValue: getInt(messageValue.substring(26, 28)) | |
| }, { | |
| measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(messageValue.substring(28, 30)) | |
| } | |
| ] | |
| } | |
| function getMotionSetting (str) { | |
| return [ | |
| {measurementId: '3946', type: 'Motion Enable', measurementValue: getInt(str.substring(0, 2))}, | |
| {measurementId: '3947', type: 'Any Motion Threshold', measurementValue: getSensorValue(str.substring(2, 6), 1)}, | |
| {measurementId: '3948', type: 'Motion Start Interval', measurementValue: getMinsByMin(str.substring(6, 10))}, | |
| ] | |
| } | |
| function getStaticSetting (str) { | |
| return [ | |
| {measurementId: '3949', type: 'Static Enable', measurementValue: getInt(str.substring(0, 2))}, | |
| {measurementId: '3950', type: 'Device Static Timeout', measurementValue: getMinsByMin(str.substring(2, 6))} | |
| ] | |
| } | |
| function getShockSetting (str) { | |
| return [ | |
| {measurementId: '3951', type: 'Shock Enable', measurementValue: getInt(str.substring(0, 2))}, | |
| {measurementId: '3952', type: 'Shock Threshold', measurementValue: getInt(str.substring(2, 6))} | |
| ] | |
| } | |
| function getTempSetting (str) { | |
| return [ | |
| {measurementId: '3953', type: 'Temp Enable', measurementValue: getInt(str.substring(0, 2))}, | |
| {measurementId: '3954', type: 'Event Temp Interval', measurementValue: getMinsByMin(str.substring(2, 6))}, | |
| {measurementId: '3955', type: 'Event Temp Sample Interval', measurementValue: getSecondsByInt(str.substring(6, 10))}, | |
| {measurementId: '3956', type: 'Temp ThMax', measurementValue: getSensorValue(str.substring(10, 14), 10)}, | |
| {measurementId: '3957', type: 'Temp ThMin', measurementValue: getSensorValue(str.substring(14, 18), 10)}, | |
| {measurementId: '3958', type: 'Temp Warning Type', measurementValue: getInt(str.substring(18, 20))} | |
| ] | |
| } | |
| function getLightSetting (str) { | |
| return [ | |
| {measurementId: '3959', type: 'Light Enable', measurementValue: getInt(str.substring(0, 2))}, | |
| {measurementId: '3960', type: 'Event Light Interval', measurementValue: getMinsByMin(str.substring(2, 6))}, | |
| {measurementId: '3961', type: 'Event Light Sample Interval', measurementValue: getSecondsByInt(str.substring(6, 10))}, | |
| {measurementId: '3962', type: 'Light ThMax', measurementValue: getSensorValue(str.substring(10, 14), 10)}, | |
| {measurementId: '3963', type: 'Light ThMin', measurementValue: getSensorValue(str.substring(14, 18), 10)}, | |
| {measurementId: '3964', type: 'Light Warning Type', measurementValue: getInt(str.substring(18, 20))} | |
| ] | |
| } | |
| function getShardFlag (str) { | |
| let bitStr = getByteArray(str) | |
| return { | |
| count: parseInt(bitStr.substring(0, 4), 2), | |
| index: parseInt(bitStr.substring(4), 2) | |
| } | |
| } | |
| function getBattery (batteryStr) { | |
| return loraWANV2DataFormat(batteryStr) | |
| } | |
| function getSoftVersion (softVersion) { | |
| return `${loraWANV2DataFormat(softVersion.substring(0, 2))}.${loraWANV2DataFormat(softVersion.substring(2, 4))}` | |
| } | |
| function getHardVersion (hardVersion) { | |
| return `${loraWANV2DataFormat(hardVersion.substring(0, 2))}.${loraWANV2DataFormat(hardVersion.substring(2, 4))}` | |
| } | |
| function getSecondsByInt (str) { | |
| return getInt(str) | |
| } | |
| function getMinsByMin (str) { | |
| return getInt(str) | |
| } | |
| function getSensorValue (str, dig) { | |
| if (str === '8000') { | |
| return null | |
| } else { | |
| return loraWANV2DataFormat(str, dig) | |
| } | |
| } | |
| function bytes2HexString (arrBytes) { | |
| var str = '' | |
| for (var i = 0; i < arrBytes.length; i++) { | |
| var tmp | |
| var num = arrBytes[i] | |
| if (num < 0) { | |
| tmp = (255 + num + 1).toString(16) | |
| } else { | |
| tmp = num.toString(16) | |
| } | |
| if (tmp.length === 1) { | |
| tmp = '0' + tmp | |
| } | |
| str += tmp | |
| } | |
| return str | |
| } | |
| function loraWANV2DataFormat (str, divisor = 1) { | |
| let strReverse = bigEndianTransform(str) | |
| let str2 = toBinary(strReverse) | |
| if (str2.substring(0, 1) === '1') { | |
| let arr = str2.split('') | |
| let reverseArr = arr.map((item) => { | |
| if (parseInt(item) === 1) { | |
| return 0 | |
| } else { | |
| return 1 | |
| } | |
| }) | |
| str2 = parseInt(reverseArr.join(''), 2) + 1 | |
| return '-' + str2 / divisor | |
| } | |
| return parseInt(str2, 2) / divisor | |
| } | |
| function bigEndianTransform (data) { | |
| let dataArray = [] | |
| for (let i = 0; i < data.length; i += 2) { | |
| dataArray.push(data.substring(i, i + 2)) | |
| } | |
| return dataArray | |
| } | |
| function toBinary (arr) { | |
| let binaryData = arr.map((item) => { | |
| let data = parseInt(item, 16) | |
| .toString(2) | |
| let dataLength = data.length | |
| if (data.length !== 8) { | |
| for (let i = 0; i < 8 - dataLength; i++) { | |
| data = `0` + data | |
| } | |
| } | |
| return data | |
| }) | |
| return binaryData.toString().replace(/,/g, '') | |
| } | |
| function getSOSMode (str) { | |
| return loraWANV2DataFormat(str) | |
| } | |
| function getMacAndRssiObj (pair) { | |
| let pairs = [] | |
| if (pair.length % 14 === 0) { | |
| for (let i = 0; i < pair.length; i += 14) { | |
| let mac = getMacAddress(pair.substring(i, i + 12)) | |
| if (mac) { | |
| let rssi = getInt8RSSI(pair.substring(i + 12, i + 14)) | |
| pairs.push({mac: mac, rssi: rssi}) | |
| } else { | |
| continue | |
| } | |
| } | |
| } | |
| return pairs | |
| } | |
| function getMacAddress (str) { | |
| if (str.toLowerCase() === 'ffffffffffff') { | |
| return null | |
| } | |
| let macArr = [] | |
| for (let i = 1; i < str.length; i++) { | |
| if (i % 2 === 1) { | |
| macArr.push(str.substring(i - 1, i + 1)) | |
| } | |
| } | |
| let mac = '' | |
| for (let i = 0; i < macArr.length; i++) { | |
| mac = mac + macArr[i] | |
| if (i < macArr.length - 1) { | |
| mac = mac + ':' | |
| } | |
| } | |
| return mac | |
| } | |
| function getInt8RSSI (str) { | |
| return loraWANV2DataFormat(str) | |
| } | |
| function getInt (str) { | |
| return parseInt(str, 16) | |
| } | |
| function getEventStatus (str) { | |
| // return getInt(str) | |
| let bitStr = getByteArray(str) | |
| let bitArr = [] | |
| for (let i = 0; i < bitStr.length; i++) { | |
| bitArr[i] = bitStr.substring(i, i + 1) | |
| } | |
| bitArr = bitArr.reverse() | |
| let event = [] | |
| for (let i = 0; i < bitArr.length; i++) { | |
| if (bitArr[i] !== '1') { | |
| continue | |
| } | |
| switch (i){ | |
| case 0: | |
| event.push({id:1, eventName:"Start moving event."}) | |
| break | |
| case 1: | |
| event.push({id:2, eventName:"End movement event."}) | |
| break | |
| case 2: | |
| event.push({id:3, eventName:"Motionless event."}) | |
| break | |
| case 3: | |
| event.push({id:4, eventName:"Shock event."}) | |
| break | |
| case 4: | |
| event.push({id:5, eventName:"Temperature event."}) | |
| break | |
| case 5: | |
| event.push({id:6, eventName:"Light event."}) | |
| break | |
| case 6: | |
| event.push({id:7, eventName:"SOS event."}) | |
| break | |
| case 7: | |
| event.push({id:8, eventName:"Press once event."}) | |
| break | |
| } | |
| } | |
| return event | |
| } | |
| function getByteArray (str) { | |
| let bytes = [] | |
| for (let i = 0; i < str.length; i += 2) { | |
| bytes.push(str.substring(i, i + 2)) | |
| } | |
| return toBinary(bytes) | |
| } | |
| function getWorkingMode (workingMode) { | |
| return getInt(workingMode) | |
| } | |
| function getPositioningStrategy (strategy) { | |
| return getInt(strategy) | |
| } | |
| function getUTCTimestamp(str){ | |
| return parseInt(loraWANV2PositiveDataFormat(str)) * 1000 | |
| } | |
| function loraWANV2PositiveDataFormat (str, divisor = 1) { | |
| let strReverse = bigEndianTransform(str) | |
| let str2 = toBinary(strReverse) | |
| return parseInt(str2, 2) / divisor | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment