Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Yobiiq B.V. SD-1001 Smoke Detector template #28

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/downlink/converter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "Downlink data converter for Yobiiq SD-1001",
"type": "DOWNLINK",
"debugMode": true,
"configuration": {
"scriptLang": "JS",
"decoder": null,
"tbelDecoder": null,
"encoder": "// Encode downlink data from incoming Rule Engine message\n\n// msg - JSON message payload downlink message json\n// msgType - type of message, for ex. 'ATTRIBUTES_UPDATED', 'POST_TELEMETRY_REQUEST', etc.\n// metadata - list of key-value pairs with additional data about the message\n// integrationMetadata - list of key-value pairs with additional data defined in Integration executing this converter\n\n/** Encoder **/\n\n\n// Process data from incoming message and metadata\nvar input = {\n fPort : parseInt(metadata.rc_fPort),\n data : msg,\n variables: null,\n devEui : metadata.cs_devEui,\n applicationId: metadata.cs_applicationId,\n};\n\n// Result object with encoded downlink payload\nvar result = {\n\n // downlink data content type: JSON, TEXT or BINARY (base64 format)\n contentType: \"TEXT\",\n\n // downlink data\n data: bytesToBase64(encodeDownlink(input).bytes),\n\n // Optional metadata object presented in key/value format\n metadata: {\n DevEUI: input.devEui,\n fPort: input.fPort,\n }\n\n};\n\n\nreturn result;\n\nfunction bytesToBase64(bytes) {\n var binary = '';\n for (var i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n\n/************************************************************************************************************/\n\n// Encode encodes the given object into an array of bytes. (ChirpStack v3)\n// - fPort contains the LoRaWAN fPort number\n// - obj is an object, e.g. {\"temperature\": 22.5}\n// - variables contains the device variables e.g. {\"calibration\": \"3.5\"} (both the key / value are of type string)\n// The function must return an array of bytes, e.g. [225, 230, 255, 0]\nfunction Encode(fPort, obj, variables) {\n // Constants for downlink\n var CONFIG_DOWNLINK = {\n TYPE : \"Type\",\n CONFIG : \"Config\"\n };\n\n try\n {\n if(obj[CONFIG_DOWNLINK.TYPE] == CONFIG_DOWNLINK.CONFIG)\n {\n return encodeDeviceConfiguration(obj[CONFIG_DOWNLINK.CONFIG], variables);\n }\n }catch(error)\n {\n\n }\n return [];\n}\n\n// Encode downlink function. (ChirpStack v4 , TTN)\n//\n// Input is an object with the following fields:\n// - data = Object representing the payload that must be encoded.\n// - variables = Object containing the configured device variables.\n//\n// Output must be an object with the following fields:\n// - bytes = Byte array containing the downlink payload.\nfunction encodeDownlink(input) {\n return {\n bytes: Encode(null, input.data, input.variables)\n };\n}\n\n\n/************************************************************************************************************/\n\n\nfunction encodeDeviceConfiguration(objArray, variables)\n{\n // Constants for device configuration \n var CONFIG_DEVICE = {\n PORT : 50,\n CHANNEL : parseInt(\"0xFF\", 16),\n TYPES : {\n \"reportingInterval\" : {TYPE : parseInt(\"0x03\", 16), SIZE : 2, MIN : 1, MAX : 65535,},\n \"smokeDetector\" : {TYPE : parseInt(\"0x00\", 16), SIZE : 1, MIN : 0, MAX : 1,},\n \"silenceBuzzer\" : {TYPE : parseInt(\"0x0A\", 16), SIZE : 2, MIN : 0, MAX : 65535,},\n \"confirmedUplink\" : {TYPE : parseInt(\"0x01\", 16), SIZE : 1, MIN : 0, MAX : 1,},\n }\n };\n var encoded = [];\n var index = 0;\n var field = [\"Param\", \"Value\"];\n try\n {\n for(var i=0; i<objArray.length; i=i+1)\n {\n var obj = objArray[i];\n var config = CONFIG_DEVICE.TYPES[obj[field[0]]];\n var value = obj[field[1]];\n if(obj[field[1]] >= config.MIN && obj[field[1]] <= config.MAX)\n {\n encoded[index] = CONFIG_DEVICE.CHANNEL;\n index = index + 1;\n encoded[index] = config.TYPE;\n index = index + 1;\n if(config.SIZE == 1)\n {\n encoded[index] = value;\n index = index + 1;\n }else if(config.SIZE == 2)\n {\n switch(config.TYPE)\n {\n case 3: // reporting interval\n var lowByte = value % 256;\n encoded[index] = ((lowByte & parseInt(\"0x0F\", 16)) << 4) + (lowByte >> 4);\n index = index + 1;\n encoded[index] = (value >> 8) % 256;\n index = index + 1;\n break;\n default:\n encoded[index] = (value >> 8) % 256;\n index = index + 1;\n encoded[index] = value % 256;\n index = index + 1;\n break;\n }\n }\n }else\n {\n // Error\n return [];\n }\n }\n }catch(error)\n {\n // Error\n return [];\n }\n return encoded;\n}",
"tbelEncoder": "// Encode downlink data from incoming Rule Engine message\n\n// msg - JSON message payload downlink message json\n// msgType - type of message, for ex. 'ATTRIBUTES_UPDATED', 'POST_TELEMETRY_REQUEST', etc.\n// metadata - list of key-value pairs with additional data about the message\n// integrationMetadata - list of key-value pairs with additional data defined in Integration executing this converter\n\n/** Encoder **/\n\n\n// Process data from incoming message and metadata\nvar input = {\n fPort : parseInt(metadata.rc_fPort),\n data : msg,\n variables: null,\n devEui : metadata.cs_devEui,\n applicationId: metadata.cs_applicationId,\n};\nvar bytes = encodeDownlink(input).bytes;\n// Result object with encoded downlink payload\nvar result = {\n\n // downlink data content type: JSON, TEXT or BINARY (base64 format)\n contentType: \"TEXT\",\n\n // downlink data\n data: btoa(bytesToHex(bytes)),\n\n // Optional metadata object presented in key/value format\n metadata: {\n DevEUI: \"401e471381e910ad\",//input.devEui,\n fPort: input.fPort,\n }\n\n};\n\nfunction getDigitStringArrayEvenFormat(bytes, index, size)\n{\n var hexString = [];\n for(var i=0; i<size; i=i+1)\n {\n hexString.push(bytes[index+i].toString(16));\n }\n return hexString.map(toEvenHEX);\n}\n\nfunction toEvenHEX(hex)\n{\n if(hex.length == 1)\n {\n return \"0\"+hex;\n }\n return hex;\n}\n\n\n/************************************************************************************************************/\n\n// Encode encodes the given object into an array of bytes. (ChirpStack v3)\n// - fPort contains the LoRaWAN fPort number\n// - obj is an object, e.g. {\"temperature\": 22.5}\n// - variables contains the device variables e.g. {\"calibration\": \"3.5\"} (both the key / value are of type string)\n// The function must return an array of bytes, e.g. [225, 230, 255, 0]\nfunction Encode(fPort, obj, variables) {\n // Constants for downlink\n var CONFIG_DOWNLINK = {\n TYPE : \"Type\",\n CONFIG : \"Config\"\n };\n\n try\n {\n if(obj[CONFIG_DOWNLINK.TYPE] == CONFIG_DOWNLINK.CONFIG)\n {\n return encodeDeviceConfiguration(obj[CONFIG_DOWNLINK.CONFIG], variables);\n }\n }catch(error)\n {\n return [error.message];\n }\n return [];\n}\n\n// Encode downlink function. (ChirpStack v4 , TTN)\n//\n// Input is an object with the following fields:\n// - data = Object representing the payload that must be encoded.\n// - variables = Object containing the configured device variables.\n//\n// Output must be an object with the following fields:\n// - bytes = Byte array containing the downlink payload.\nfunction encodeDownlink(input) {\n return {\n bytes: Encode(null, input.data, input.variables)\n };\n}\n\n/************************************************************************************************************/\n\nfunction encodeDeviceConfiguration(obj, variables)\n{\n // Constants for device configuration \n var CONFIG_DEVICE = {\n PORT : 50,\n CHANNEL : parseInt(\"0xFF\", 16),\n TYPES : {\n \"ReportingInterval\" : {TYPE : parseInt(\"0x03\", 16), SIZE : 2, MIN : 1, MAX : 65535,},\n \"SmokeDetector\" : {TYPE : parseInt(\"0x00\", 16), SIZE : 1, MIN : 0, MAX : 1,},\n \"SilenceBuzzer\" : {TYPE : parseInt(\"0x0A\", 16), SIZE : 2, MIN : 0, MAX : 65535,},\n \"ConfirmedUplink\" : {TYPE : parseInt(\"0x01\", 16), SIZE : 1, MIN : 0, MAX : 1,},\n }\n };\n\n var encoded = [];\n var index = 0;\n var field = [\"Param\", \"Value\"];\n try\n {\n var config = CONFIG_DEVICE.TYPES[obj[field[0]]];\n var value = obj[field[1]];\n if(obj[field[1]] >= config.MIN && obj[field[1]] <= config.MAX)\n {\n encoded[index] = CONFIG_DEVICE.CHANNEL;\n index = index + 1;\n encoded[index] = config.TYPE;\n index = index + 1;\n if(config.SIZE == 1)\n {\n encoded[index] = value;\n index = index + 1;\n }else if(config.SIZE == 2)\n {\n switch(config.TYPE)\n {\n case 3: // reporting interval\n var lowByte = value % 256;\n encoded[index] = ((lowByte & parseInt(\"0x0F\", 16)) << 4) + (lowByte >> 4);\n index = index + 1;\n encoded[index] = (value >> 8) % 256;\n index = index + 1;\n break;\n default:\n encoded[index] = (value >> 8) % 256;\n index = index + 1;\n encoded[index] = value % 256;\n index = index + 1;\n break;\n }\n }\n }else\n {\n // Error\n return [];\n }\n }catch(error)\n {\n // Error\n return [];\n }\n return encoded;\n}\n\n\n\n\n\n\n\nreturn result;",
"updateOnlyKeys": [
"tenantId",
"tenantName",
"applicationId",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devEui",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"battery",
"pir",
"daylight",
"confirmed",
"gatewayId",
"channel",
"rfChain",
"crcStatus"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
9 changes: 9 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/downlink/payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"msg": {
"Type": "Config",
"Config": [{
"Param": "smokeDetector",
"Value": 1
}]
}
}
9 changes: 9 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/downlink/payload_01.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"msg": {
"Type": "Config",
"Config": [{
"Param": "smokeDetector",
"Value": 0
}]
}
}
8 changes: 8 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/downlink/result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"contentType": "TEXT",
"data": "/wAA",
"metadata": {
"DevEUI": "fc48c9fffe001141",
"fPort": 50
}
}
8 changes: 8 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/downlink/result_01.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"contentType": "TEXT",
"data": "/wAA",
"metadata": {
"DevEUI": "fc48c9fffe001141",
"fPort": 50
}
}
37 changes: 37 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/uplink/converter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "Uplink data converter for Yobiiq SD-1001",
"type": "UPLINK",
"debugMode": true,
"configuration": {
"scriptLang": "TBEL",
"decoder": null,
"tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName;\nvar deviceType = 'Smoke Detectors';\nvar groupName = 'Smoke Detectors';\n// var customerName = 'Customer A';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// --- attributes and telemetry objects ---\nvar telemetry = {};\nvar attributes = {};\n\n// --- Timestamp parsing\nvar dateString = data.time;\nvar timestamp = -1;\nif (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n}\n// If we cannot parse timestamp - we will use the current timestamp\nif (timestamp == -1) {\n timestamp = Date.now();\n}\n\n// You can exclude some keys from the result\nvar excludeFromAttributesList = [\"deviceName\", \"rxInfo\", \"confirmed\", \"data\", \"deduplicationId\",\"time\", \"adr\", \"dr\", \"fCnt\"];\nvar excludeFromTelemetryList = [\"data\", \"deviceInfo\", \"txInfo\", \"devAddr\", \"adr\", \"time\", \"fPort\", \"region_common_name\", \"region_config_id\", \"deduplicationId\", \"deviceModel\", \"codecVersion\", \"productCode\", \"manufacturer\", \"nsTime\", \"uplinkId\"];\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar telemetryData = toFlatMap(data, excludeFromTelemetryList, false);\nvar attributesData = toFlatMap(data, excludeFromAttributesList, false);\n\n\ntelemetry.putAll(telemetryData);\nattributes.putAll(attributesData);\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: attributes,\n telemetry: {\n ts: timestamp,\n values: telemetry\n }\n};\n\nreturn result;",
"encoder": null,
"tbelEncoder": null,
"updateOnlyKeys": [
"tenantId",
"tenantName",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"battery",
"pir",
"daylight",
"confirmed",
"gatewayId",
"channel",
"rfChain",
"crcStatus"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
3 changes: 3 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/uplink/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"integrationName": "ChirpStack integration"
}
Empty file.
59 changes: 59 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/uplink/payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"deduplicationId": "1fd53d79-2c7d-4a63-8bfa-5c9ce2179d53",
"time": "2024-10-31T15:03:19.746383778+00:00",
"deviceInfo": {
"tenantId": "2cc5244e-a4e4-48c6-aadc-df9ccde937b2",
"tenantName": "Development",
"applicationId": "075f3db0-9141-4bd2-a355-b5e3e4418b2f",
"applicationName": "SD1001",
"deviceProfileId": "74946a3b-98b1-4fd0-a332-7452f75ebed6",
"deviceProfileName": "SD1001",
"deviceName": "DC0EE928",
"devEui": "8df95720000ee928",
"deviceClassEnabled": "CLASS_C",
"tags": {}
},
"devAddr": "01851d7d",
"adr": true,
"dr": 5,
"fCnt": 1,
"fPort": 8,
"confirmed": false,
"data": "AXVkAgsAAwAABAAABQAABgAA",
"object": {
"deviceModel": "SD-1001",
"batteryLevelInPercentage": 100.0,
"productCode": "1002015",
"manufacturer": "YOBIIQ B.V.",
"codecVersion": "1.1.0",
"lowBatteryAlarm": "Normal",
"faultAlarm": "Normal",
"powerEvent": "AC Power Off",
"smokeAlarm": "Normal"
},
"rxInfo": [{
"gatewayId": "647fdafffe012d39",
"uplinkId": 61045,
"nsTime": "2024-10-31T15:03:19.536132369+00:00",
"rssi": -33,
"snr": 10.0,
"channel": 5,
"location": {},
"context": "EstX5A==",
"metadata": {
"region_config_id": "eu868",
"region_common_name": "EU868"
},
"crcStatus": "CRC_OK"
}],
"txInfo": {
"frequency": 867500000,
"modulation": {
"lora": {
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
}
}
}
}
Empty file.
59 changes: 59 additions & 0 deletions VENDORS/Yobiiq/SD1001/ChirpStack/uplink/payload_01.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"deduplicationId": "6c567e63-65f4-4ee4-a4e3-6ca9ebfc57ee",
"time": "2024-10-31T07:53:19.663960865+00:00",
"deviceInfo": {
"tenantId": "2cc5244e-a4e4-48c6-aadc-df9ccde937b2",
"tenantName": "Development",
"applicationId": "075f3db0-9141-4bd2-a355-b5e3e4418b2f",
"applicationName": "SD1001",
"deviceProfileId": "74946a3b-98b1-4fd0-a332-7452f75ebed6",
"deviceProfileName": "SD1001",
"deviceName": "SD1001-1",
"devEui": "fc48c9fffe001141",
"deviceClassEnabled": "CLASS_C",
"tags": {}
},
"devAddr": "01e1a53a",
"adr": true,
"dr": 5,
"fCnt": 15,
"fPort": 8,
"confirmed": true,
"data": "AXVkAgsBAwAABAAABQAABgAABwAA",
"object": {
"powerEvent": "AC Power On",
"lowBatteryAlarm": "Normal",
"codecVersion": "1.1.0",
"batteryLevelInPercentage": 100.0,
"smokeAlarm": "Normal",
"deviceModel": "SD-1001",
"productCode": "1002015",
"manufacturer": "YOBIIQ B.V.",
"faultAlarm": "Normal"
},
"rxInfo": [{
"gatewayId": "647fdafffe012d39",
"uplinkId": 50609,
"nsTime": "2024-10-31T07:53:19.454727220+00:00",
"rssi": -67,
"snr": 9.2,
"channel": 7,
"location": {},
"context": "EPzY5A==",
"metadata": {
"region_common_name": "EU868",
"region_config_id": "eu868"
},
"crcStatus": "CRC_OK"
}],
"txInfo": {
"frequency": 867900000,
"modulation": {
"lora": {
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
}
}
}
}
Loading
Loading