This document describes how to integrate the YOBIIQ SD-1001 into ThingsBoard.
Last updated
Introduction
The SD-1001 is a 230V mains powered smoke detector, with backup battery.
This IoT smoke detector offers smart smoke detector capabilities, ensuring reliable fire safety solutions for both residential and commercial use. With wireless smoke alarm functionality, annual inspections are conducted remotely via LoRaWAN®, eliminating the need for on-site appointments, thus streamlining the process and improving efficiency.
This document is based on the assumption that you use the ThingsBoard Professional Edition.
Prerequisites
To succesfully complete the integration of the SD-1001 into ThingsBoard Professional Edition you will need the following;
LoRaWAN® Gateway
Network Server account
ThingsBoard Professional Edition account
LoRaWAN Gateway
We have no specific hardware requirements for a LoRaWAN® Gateway, it should however have it's packet forwarder setup to Chirpstack or Things Network to be covered within this Integration Guide.
Other Network Servers are not covered in this Integration Guide.
Connecting a Device
After connecting it to the network server we can provision the device to ThingsBoard.
Chirpstack
If you are using the ThingsBoard Integration library (recommended) you can skip pasting the codec into Chirpstack.
Login to your Chirpstack environment.
Create a Device profile, Go to the Device Profiles and click on the Add device profile button.
Fill in the form fields for the Device Profile and click the Submit button.
Create an Application, Go to the Applications page and click on the Add applicationbutton.
Fill in the form fields and click the Submit button.
Click on the Application you have created in the previous step, and click on the Add device button.
Fill in the form fields, in the first screen you will be required to enter your Device EUI (DevEUI), after you click the submit button you will be required to enter the Application key.
API Key
Now that we have added the device to Chirpstack we also need to create an API key within Chirpstack.
This API key is used to make a secure login connection between Chirpstack and your ThingsBoard instance.
Things Network
If you are using the ThingsBoard Integration library (recommended) you can skip pasting the Payload formatter into Things Network.
Login to your Things Industries environment.
Add an application, Go to the Applications and click on the Add application button.
Fill in the form fields for the Application and click the Create application button.
Register the End device, Go to the End devices and click on the Add device button.
Fill in the form fields, in the first screen you will be required to enter your Join EUI, after you click the submit button you will be required to enter the other related parameters.
API Key
Now that we have added the device to Things Industries we also need to create an API key within Things Industries.
This API key is used to make a secure login connection between Things Industries and your ThingsBoard instance.
Device Integration
Creating a Device Profile
If you want to utilize the full potential of the Smoke Detectors we advice you to use the Smoke Detectors in combination with our Smoke Detection Monitoring Dashboard.
Device Profile
You can import a Device Profile or create one yourself.
The Device Profile is also available on our Github.
After you have successfully imported the Device Profile, you are returned to the Device Profile Overview.
Creating the Device Integration
The device integration can be imported from the ThingsBoard Device Repository.
After you have entered the information above and click next you will be able to make a choice for the Uplink data converter, if you choose "Library" in this section you are able to pre-load the Uplink and Downlink data converter from the ThingsBoard Library.
ChirpStack
If you have selected the ChirpStack integration type you will now be asked the application server URL and the API key.
If you have followed this Integration Guide you have saved the API key somewhere, in this step you will need to paste that API key.
Things Industries
If you have selected the Things Industries integration type you will now be asked the application server URL and the API key.
If you have followed this Integration Guide you have saved the MQTT authentication information somewhere, in this step you will need to paste that information.
Uplink Data Converter
The uplink data converter is loaded automatically when you use ThingsBoard Integration Library.
The Uplink Data Converter is available from within the ThingsBoard Device Integration.
If your version of ThingsBoard does not support the Device Integration yet, you can use the following Uplink Data Converter.
{
"name": "ChirpStack Uplink data converter for Yobiiq SD-1001",
"type": "UPLINK",
"debugMode": true,
"configuration": {
"scriptLang": "TBEL",
"decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\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// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;",
"tbelDecoder": "var data = decodeToJson(payload);\r\nvar deviceName = data.deviceInfo.deviceName;\r\nvar deviceType = 'Smoke Detector';\r\nvar groupName = 'Smoke Detectors';\r\nvar customerName = data.deviceInfo.tags.customerName;\r\n// use assetName and assetType instead of deviceName and deviceType\r\n// to automatically create assets instead of devices.\r\n// var assetName = 'Asset A';\r\n// var assetType = 'building';\r\n\r\n// --- attributes and telemetry objects ---\r\nvar telemetry = {};\r\nvar attributes = {};\r\n\r\n// --- Timestamp parsing\r\nvar dateString = data.time;\r\nvar timestamp = -1;\r\nif (dateString != null) {\r\n timestamp = new Date(dateString).getTime();\r\n if (timestamp == -1) {\r\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\r\n var millisecondsEndIndex = dateString.lastIndexOf('+');\r\n if (millisecondsEndIndex == -1) {\r\n millisecondsEndIndex = dateString.lastIndexOf('Z');\r\n }\r\n if (millisecondsEndIndex == -1) {\r\n millisecondsEndIndex = dateString.lastIndexOf('-');\r\n }\r\n if (millisecondsEndIndex == -1) {\r\n if (dateString.length >= secondsSeparatorIndex + 3) {\r\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\r\n }\r\n } else {\r\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\r\n dateString.substring(millisecondsEndIndex, dateString.length);\r\n }\r\n timestamp = new Date(dateString).getTime();\r\n }\r\n}\r\n// If we cannot parse timestamp - we will use the current timestamp\r\nif (timestamp == -1) {\r\n timestamp = Date.now();\r\n}\r\n\r\nvar telemetryData = {};\r\nvar attributesData = {};\r\nattributesData.putAll(data.deviceInfo);\r\nattributesData.remove('deviceName');\r\n\r\n// empty input uplink\r\nvar input = {\r\n fPort: null,\r\n bytes: [],\r\n variables: null,\r\n};\r\n\r\n// empty decoded uplink\r\nvar up = {\r\n timestamp : timestamp,\r\n object: {},\r\n fPort: null\r\n};\r\n\r\nif(data.fPort != null)\r\n{\r\n telemetryData.fPort = data.fPort;\r\n telemetryData.confirmed = data.confirmed;\r\n telemetryData.fCnt = data.fCnt;\r\n telemetryData.dr = data.dr;\r\n\r\n input.fPort = data.fPort;\r\n input.bytes = hexStringToIntList(base64ToHex(data.data));\r\n\r\n // Decode uplink\r\n up.fPort = input.fPort;\r\n up.object = decodeUplink(input).data;\r\n}\r\n\r\nvar decoderAttributes = getVersionControl();\r\nvar decoderTelemetry = {};\r\n\r\nforeach(element: up.object.entrySet())\r\n{\r\n if(element.key == 'warning') {\r\n decoderAttributes[element.key] = element.value;\r\n }else if(element.key == 'info')\r\n {\r\n decoderAttributes[element.key] = element.value;\r\n }else{\r\n decoderTelemetry[element.key] = element.value;\r\n }\r\n}\r\ntelemetry.putAll(telemetryData);\r\nattributes.putAll(attributesData);\r\ntelemetry.putAll(decoderTelemetry);\r\nattributes.putAll(decoderAttributes);\r\n\r\nvar result = {\r\n deviceName: deviceName,\r\n deviceType: deviceType,\r\n // assetName: assetName,\r\n // assetType: assetType,\r\n groupName: groupName,\r\n attributes: attributes,\r\n telemetry: {\r\n ts: timestamp,\r\n values: telemetry\r\n }\r\n};\r\nif(customerName != null)\r\n{\r\n result.customerName = customerName;\r\n}\r\n\r\nreturn result;\r\n\r\n\r\n/**************************** Yobiiq Decoder *******************************/\r\n\r\nfunction isBasicInformation(bytes, fPort)\r\n{\r\n if(fPort == 50)\r\n {\r\n return true;\r\n }\r\n // Example: ff090100 ff0a0102 ff162404152795 ff0f02 ff0b01\r\n if(bytes[0] == 0xFF &&\r\n bytes[4] == 0xFF &&\r\n bytes[8] == 0xFF\r\n )\r\n {\r\n return true;\r\n }\r\n return false;\r\n}\r\n\r\nfunction decodeBasicInformation(bytes)\r\n{\r\n // Configuration constants for device basic info\r\n var CONFIG_INFO = {\r\n FPORT : 50,\r\n CHANNEL : 0xFF,\r\n TYPES : {\r\n \"0x09\" : {SIZE : 2, NAME : \"hardwareVersion\", DIGIT: false},\r\n \"0x0A\" : {SIZE : 2, NAME : \"firmwareVersion\", DIGIT: false},\r\n \"0x16\" : {SIZE : 5, NAME : \"deviceSerialNumber\", DIGIT: true},\r\n \"0x0F\" : {SIZE : 1, NAME : \"deviceClass\",\r\n VALUES : {\r\n \"0x00\" : \"Class A\",\r\n \"0x01\" : \"Class B\",\r\n \"0x02\" : \"Class C\",\r\n },\r\n },\r\n \"0x0B\" : {SIZE : 1, NAME : \"powerEvent\",\r\n VALUES : {\r\n \"0x00\" : \"AC Power Off\",\r\n \"0x01\" : \"AC Power On\",\r\n },\r\n },\r\n },\r\n WARNING_NAME : \"warning\",\r\n ERROR_NAME : \"error\",\r\n INFO_NAME : \"info\"\r\n };\r\n var LENGTH = bytes.length;\r\n var decoded = {};\r\n var index = 0;\r\n var channel = 0;\r\n var type = \"\";\r\n var size = 0;\r\n if(LENGTH == 1)\r\n {\r\n if(bytes[0] == 0)\r\n {\r\n decoded[CONFIG_INFO.INFO_NAME] = \"Downlink command succeeded\";\r\n\r\n } else if(bytes[0] == 1)\r\n {\r\n decoded[CONFIG_INFO.WARNING_NAME] = \"Downlink command failed\";\r\n }\r\n return decoded;\r\n }\r\n while(index < LENGTH)\r\n {\r\n channel = bytes[index];\r\n index = index + 1;\r\n if(channel != CONFIG_INFO.CHANNEL)\r\n {\r\n channel = \"0x\" + byteToEvenHex(channel).toUpperCase();\r\n exceptedValue = \"0x\" + byteToEvenHex(CONFIG_INFO.CHANNEL).toUpperCase();\r\n decoded.error = \"channel \"+ channel + \" should be \" + exceptedValue;\r\n return decoded;\r\n }\r\n // Type of basic information\r\n type = \"0x\" + byteToEvenHex(bytes[index]).toUpperCase();\r\n index = index + 1;\r\n var info = CONFIG_INFO.TYPES[type];\r\n if(info == null)\r\n {\r\n decoded.error = \"can't decode type \"+ type;\r\n return decoded;\r\n }\r\n size = info.SIZE;\r\n // Decoding\r\n var value = 0;\r\n if(size != 0)\r\n {\r\n if(info.DIGIT != null)\r\n {\r\n if(info.DIGIT == false)\r\n {\r\n // Decode into \"V\" + DIGIT STRING + \".\" DIGIT STRING format\r\n value = getDigitStringArrayNoFormat(bytes, index, size);\r\n value = \"V\" + value[0] + \".\" + value[1];\r\n }else\r\n {\r\n // Decode into DIGIT STRING format\r\n value = getDigitStringArrayEvenFormat(bytes, index, size).join(\"\");\r\n value = parseLong(value, 10);\r\n }\r\n }\r\n else if(info.VALUES != null)\r\n {\r\n // Decode into HEX STRING (VALUES specified in CONFIG_INFO)\r\n value = \"0x\" + byteToEvenHex(bytes[index]).toUpperCase();\r\n value = info.VALUES[value];\r\n }else\r\n {\r\n // Decode into DECIMAL format\r\n value = parseBytesToInt(bytes, index, size);\r\n }\r\n decoded[info.NAME] = value;\r\n index = index + size;\r\n }\r\n }\r\n\r\n return decoded;\r\n}\r\n\r\nfunction decodeDeviceData(bytes)\r\n{\r\n // Configuration constants for data registers\r\n var CONFIG_DATA = {\r\n FPORT : 8,\r\n CHANNELS : {\r\n \"0x01\" : {SIZE : 1, NAME : \"batteryLevelInPercentage\",},\r\n \"0x02\" : {SIZE : 1, NAME : \"powerEvent\",\r\n VALUES : {\r\n \"0x00\" : \"AC Power Off\",\r\n \"0x01\" : \"AC Power On\",\r\n },\r\n },\r\n \"0x03\" : {SIZE : 1, NAME : \"lowBatteryAlarm\",\r\n VALUES : {\r\n \"0x00\" : \"Normal\",\r\n \"0x01\" : \"Alarm\",\r\n },\r\n },\r\n \"0x04\" : {SIZE : 1, NAME : \"faultAlarm\",\r\n VALUES : {\r\n \"0x00\" : \"Normal\",\r\n \"0x01\" : \"Alarm\",\r\n },\r\n },\r\n \"0x05\" : {SIZE : 1, NAME : \"smokeAlarm\",\r\n VALUES : {\r\n \"0x00\" : \"Normal\",\r\n \"0x01\" : \"Alarm\",\r\n },\r\n },\r\n \"0x06\" : {SIZE : 1, NAME : \"interconnectAlarm\",\r\n VALUES : {\r\n \"0x00\" : \"Normal\",\r\n \"0x01\" : \"Alarm\",\r\n },\r\n },\r\n \"0x07\" : {SIZE : 1, NAME : \"testButtonPressed\",\r\n VALUES : {\r\n \"0x00\" : \"Normal\",\r\n \"0x01\" : \"Pushed\",\r\n },\r\n },\r\n },\r\n WARNING_NAME : \"warning\",\r\n ERROR_NAME : \"error\",\r\n INFO_NAME : \"info\"\r\n };\r\n var LENGTH = bytes.length;\r\n var decoded = {};\r\n var index = 0;\r\n var channel = \"\";\r\n var type = 0;\r\n var size = 0;\r\n if(LENGTH == 1)\r\n {\r\n if(bytes[0] == 0)\r\n {\r\n decoded[CONFIG_DATA.INFO_NAME] = \"Downlink command succeeded\";\r\n\r\n } else if(bytes[0] == 1)\r\n {\r\n decoded[CONFIG_DATA.WARNING_NAME] = \"Downlink command failed\";\r\n }\r\n return decoded;\r\n }\r\n while(index < LENGTH)\r\n {\r\n // Channel of device data\r\n channel = \"0x\" + byteToEvenHex(bytes[index]).toUpperCase();\r\n index = index + 1;\r\n // Type of device data\r\n type = bytes[index];\r\n index = index + 1;\r\n\r\n // No type checking\r\n\r\n var config = CONFIG_DATA.CHANNELS[channel];\r\n if(config == null)\r\n {\r\n decoded.error = \"can't decode channel \"+ channel;\r\n return decoded;\r\n }\r\n size = config.SIZE;\r\n // Decoding\r\n var value = 0;\r\n if(config.VALUES != null)\r\n {\r\n // Decode into STRING (VALUES specified in CONFIG_DATA)\r\n value = \"0x\" + byteToEvenHex(bytes[index]).toUpperCase();\r\n value = config.VALUES[value];\r\n }else\r\n {\r\n // Decode into DECIMAL format\r\n value = parseBytesToInt(bytes, index, size);\r\n }\r\n decoded[config.NAME] = value;\r\n index = index + size;\r\n }\r\n\r\n return decoded;\r\n}\r\n\r\nfunction getDigitStringArrayNoFormat(bytes, index, size)\r\n{\r\n var hexString = [];\r\n for(var i=0; i<size; i=i+1)\r\n {\r\n hexString.push(Integer.toString(bytes[index+i], 16));\r\n }\r\n return hexString\r\n}\r\n\r\nfunction getDigitStringArrayEvenFormat(bytes, index, size)\r\n{\r\n var hexString = [];\r\n for(var i=0; i<size; i=i+1)\r\n {\r\n hexString.push(byteToEvenHex(bytes[index+i]));\r\n }\r\n return hexString;\r\n}\r\n\r\nfunction toEvenHEX(hex)\r\n{\r\n if(hex.length == 1)\r\n {\r\n return \"0\"+hex;\r\n }\r\n return hex;\r\n}\r\n\r\nfunction byteToEvenHex(val)\r\n{\r\n return toEvenHEX(Integer.toString(val, 16));\r\n}\r\n\r\nfunction hexStringToIntList(hexString)\r\n{\r\n var bytes = [];\r\n var hexByte = \"\";\r\n for(var i=0; i<hexString.length; i=i+2)\r\n {\r\n hexByte = hexString[i] + hexString[i+1];\r\n bytes.push(parseHexToInt(hexByte));\r\n }\r\n return bytes;\r\n}\r\n\r\n\r\n/************************************************************************************************************/\r\n\r\n// Decode decodes an array of bytes into an object. (ChirpStack v3)\r\n// - fPort contains the LoRaWAN fPort number\r\n// - bytes is an array of bytes, e.g. [225, 230, 255, 0]\r\n// - variables contains the device variables e.g. {\"calibration\": \"3.5\"} (both the key / value are of type string)\r\n// The function must return an object, e.g. {\"temperature\": 22.5}\r\nfunction Decode(fPort, bytes, variables) \r\n{\r\n var decoded = {};\r\n if(isBasicInformation(bytes, fPort))\r\n {\r\n decoded = decodeBasicInformation(bytes);\r\n }else\r\n {\r\n decoded = decodeDeviceData(bytes);\r\n }\r\n return decoded;\r\n}\r\n\r\n// Decode uplink function. (ChirpStack v4 , TTN)\r\n//\r\n// Input is an object with the following fields:\r\n// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]\r\n// - fPort = Uplink fPort.\r\n// - variables = Object containing the configured device variables.\r\n//\r\n// Output must be an object with the following fields:\r\n// - data = Object representing the decoded payload.\r\nfunction decodeUplink(input) {\r\n return {\r\n data: Decode(input.fPort, input.bytes, input.variables)\r\n };\r\n}\r\n\r\nfunction getVersionControl()\r\n{\r\n // Version Control\r\n var VERSION_CONTROL = {\r\n CODEC : {VERSION: \"1.1.0\", NAME: \"codecVersion\"},\r\n DEVICE: {MODEL : \"SD-1001\", NAME: \"deviceModel\"},\r\n PRODUCT: {CODE : \"1002015\", NAME: \"productCode\"},\r\n MANUFACTURER: {COMPANY : \"YOBIIQ B.V.\", NAME: \"manufacturer\"},\r\n };\r\n var decoded = {};\r\n decoded[VERSION_CONTROL.CODEC.NAME] = VERSION_CONTROL.CODEC.VERSION;\r\n decoded[VERSION_CONTROL.DEVICE.NAME] = VERSION_CONTROL.DEVICE.MODEL;\r\n decoded[VERSION_CONTROL.PRODUCT.NAME] = VERSION_CONTROL.PRODUCT.CODE;\r\n decoded[VERSION_CONTROL.MANUFACTURER.NAME] = VERSION_CONTROL.MANUFACTURER.COMPANY;\r\n return decoded;\r\n}",
"encoder": null,
"tbelEncoder": null,
"updateOnlyKeys": [
"tenantId",
"tenantName",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"battery",
"confirmed",
"gatewayId",
"channel",
"rfChain",
"crcStatus",
"codecVersion",
"manufacturer"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
Downlink Data Converter
The Downlink Data Converter is available from within the ThingsBoard Device Integration.
If your version of ThingsBoard does not support the Device Integration yet, you can use the following Downlink Data Converter.
{
"name": "ChirpStack Downlink data converter for Yobiiq SD-1001",
"type": "DOWNLINK",
"debugMode": true,
"configuration": {
"scriptLang": "TBEL",
"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\nvar data = {};\n\n// Process data from incoming message and metadata\n\ndata.tempFreq = msg.temperatureUploadFrequency;\ndata.humFreq = msg.humidityUploadFrequency;\n\ndata.devSerialNumber = metadata['ss_serialNumber'];\n\n// Result object with encoded downlink payload\nvar result = {\n\n // downlink data content type: JSON, TEXT or BINARY (base64 format)\n contentType: \"JSON\",\n\n // downlink data\n data: JSON.stringify(data),\n\n // Optional metadata object presented in key/value format\n metadata: {\n topic: metadata['deviceType']+'/'+metadata['deviceName']+'/upload'\n }\n\n};\n\nreturn result;",
"tbelEncoder": "// Process data from incoming message and metadata\r\nvar input = {\r\n fPort : parseInt(metadata.rc_fPort), // from rule chain\r\n confirmed : metadata.rc_confirmed === \"true\" ? true : false, // from rule chain\r\n data : msg,\r\n variables: null,\r\n devEui : metadata.cs_devEui, // from client scope\r\n applicationId: metadata.cs_applicationId, // from client scope\r\n};\r\n\r\n// Result object with encoded downlink payload\r\nvar result = {\r\n\r\n // downlink data content type: JSON, TEXT or BINARY (base64 format)\r\n contentType: \"TEXT\",\r\n\r\n // downlink data\r\n data: bytesToBase64(encodeDownlink(input).bytes),\r\n\r\n // Optional metadata object presented in key/value format\r\n metadata: {\r\n eui: input.devEui,\r\n DevEUI: input.devEui,\r\n fPort: input.fPort,\r\n }\r\n\r\n};\r\n\r\n\r\nreturn result;\r\n\r\n\r\n\r\n/************************************************************************************************************/\r\n\r\n// Encode encodes the given object into an array of bytes. (ChirpStack v3)\r\n// - fPort contains the LoRaWAN fPort number\r\n// - obj is an object, e.g. {\"temperature\": 22.5}\r\n// - variables contains the device variables e.g. {\"calibration\": \"3.5\"} (both the key / value are of type string)\r\n// The function must return an array of bytes, e.g. [225, 230, 255, 0]\r\nfunction Encode(fPort, obj, variables) {\r\n // Constants for downlink\r\n var CONFIG_DOWNLINK = {\r\n TYPE : \"Type\",\r\n CONFIG : \"Config\"\r\n };\r\n\r\n if(obj[CONFIG_DOWNLINK.TYPE] == CONFIG_DOWNLINK.CONFIG)\r\n {\r\n return encodeDeviceConfiguration(obj[CONFIG_DOWNLINK.CONFIG]);\r\n }\r\n return [];\r\n}\r\n\r\n// Encode downlink function. (ChirpStack v4 , TTN)\r\n//\r\n// Input is an object with the following fields:\r\n// - data = Object representing the payload that must be encoded.\r\n// - variables = Object containing the configured device variables.\r\n//\r\n// Output must be an object with the following fields:\r\n// - bytes = Byte array containing the downlink payload.\r\nfunction encodeDownlink(input) {\r\n return {\r\n bytes: Encode(null, input.data, input.variables)\r\n };\r\n}\r\n\r\n\r\n/************************************************************************************************************/\r\n\r\n\r\nfunction encodeDeviceConfiguration(configs)\r\n{\r\n // Constants for device configuration \r\n var CONFIG_DEVICE = {\r\n FPORT : 50,\r\n CHANNEL : 0xFF,\r\n TYPES : {\r\n \"reportingInterval\" : {TYPE : 0x03, SIZE : 2, MIN : 1, MAX : 65535,},\r\n \"smokeDetector\" : {TYPE : 0x00, SIZE : 1, MIN : 0, MAX : 1,},\r\n \"silenceBuzzer\" : {TYPE : 0x0A, SIZE : 2, MIN : 0, MAX : 65535,},\r\n \"confirmedUplink\" : {TYPE : 0x01, SIZE : 1, MIN : 0, MAX : 1,},\r\n }\r\n };\r\n var encoded = [];\r\n\r\n foreach(configObj : configs)\r\n {\r\n var param = configObj.Param;\r\n var value = configObj.Value;\r\n var config = CONFIG_DEVICE.TYPES[param];\r\n \r\n if(config == null) {\r\n return [];\r\n }\r\n \r\n if(value < config.MIN || value > config.MAX) {\r\n return [];\r\n }\r\n \r\n encoded.push(CONFIG_DEVICE.CHANNEL);\r\n encoded.push(config.TYPE);\r\n if(config.SIZE == 1)\r\n {\r\n encoded.push(value);\r\n }else if(config.SIZE == 2)\r\n {\r\n if(config.TYPE == 3)\r\n {\r\n var lowByte = value % 256;\r\n encoded.push( ((lowByte & 0x0F) << 4) + (lowByte >> 4) );\r\n encoded.push( (value >> 8) % 256 );\r\n }else\r\n {\r\n encoded.push( (value >> 8) % 256 );\r\n encoded.push( value % 256 );\r\n }\r\n }\r\n }\r\n\r\n return encoded;\r\n}",
"updateOnlyKeys": [
"tenantId",
"tenantName",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"battery",
"confirmed",
"gatewayId",
"channel",
"rfChain",
"crcStatus",
"codecVersion",
"manufacturer"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
Verify receipt of data
At this point we have added the device integrations to ThingsBoard and the Network Server of your choosing.
If the device has sent any data, it should now appear under the Devices.
To check the receipt of data you should open the Devices which is available under your Entities. The device should be present in the devices list shown there.
You can check the latest telemetry / attributes by clicking on the Device, you can open the tab Attributes or Latest telemetry.
Dashboard
The dashboard is a complete solution to monitor all your Smoke Detectors, view the alarms and execute a remote test on the Smoke Detector.
The Dashboard is available for customers that purchased the Smoke Detector by requesting it from our Support Department.
Conclusion
With everything you have learned throughout this Integration Guide you should now be able to easily connect your YOBIIQ Smoke Detector to Thingsboard.
The , designed for LoRaWAN® remote monitoring systems, provides optimal protection through advanced IoT fire alarm technology.
The can only be operated using a LoRaWAN® gateway, so the first order of business is to connect it to a Network Server which has an integration with ThingsBoard.
To succesfully connect a device to a Network Server you will need a few device parameters.
These parameters can be located on the .
If you are using as Network Server you can follow the next steps to add your .
Past the Codec for the device, you can find the latest version on our Github Repository ()
If you are using as Network Server you can follow the next steps to add your .
Format the payloads by pasting the Uplink and Downlink codec, this can be found under Payload formatters.
The latest version of the codec can be found on our Github Repository ()
If you want a more user-friendly way of displaying the data from your you can use a ready-made dashboard from YOBIIQ.