Skip to content

Plugin file

Plugin configuration file
{
  "name": "emu_emu_prof_ii",
  "version": "1.0.0",
  "description": "3-phase energy meter with MID B+D approval for billing purpose. Connection: Direct (100A) or indirect (CT /5 and /1A). Internal clock. External or internal antenna.",
  "author": "Thinger.io",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/thinger-io/plugins.git",
    "directory": "emu-emu-prof-ii"
  },
  "metadata": {
    "name": "Emu EMU-PROF-II",
    "description": "3-phase energy meter with MID B+D approval for billing purpose. Connection: Direct (100A) or indirect (CT /5 and /1A). Internal clock. External or internal antenna.",
    "image": "assets/emu-prof-ii.png",
    "category": "devices",
    "vendor": "emu"
  },
  "resources": {
    "products": [
      {
        "description": "3-phase energy meter with MID B+D approval for billing purpose. Connection: Direct (100A) or indirect (CT /5 and /1A). Internal clock. External or internal antenna.",
        "enabled": true,
        "name": "Emu EMU-PROF-II",
        "product": "emu_emu_prof_ii",
        "profile": {
          "api": {
            "downlink": {
              "enabled": true,
              "handle_connectivity": false,
              "request": {
                "data": {
                  "path": "/downlink",
                  "payload": "{\n    \"data\"    : \"{{payload.data=\"\"}}\",\n    \"port\"    :  {{payload.port=85}},\n    \"priority\":  {{payload.priority=3}},\n    \"confirmed\" :  {{payload.confirmed=false}},\n    \"uplink\"  :  {{property.uplink}} \n}",
                  "payload_function": "",
                  "payload_type": "",
                  "plugin": "{{property.uplink.source}}",
                  "target": "plugin_endpoint"
                }
              }
            },
            "uplink": {
              "device_id_resolver": "getId",
              "enabled": true,
              "handle_connectivity": true,
              "request": {
                "data": {
                  "payload": "{{payload}}",
                  "payload_function": "",
                  "payload_type": "source_payload",
                  "resource_stream": "uplink",
                  "target": "resource_stream"
                }
              }
            }
          },
          "autoprovisions": {
            "emu_device_autoprovisioning": {
              "config": {
                "mode": "pattern",
                "pattern": "emu-prof-ii-.*"
              },
              "enabled": true
            }
          },
          "buckets": {
            "emu_emu_prof_ii_energy_data": {
              "backend": "mongodb",
              "data": {
                "payload": "{{payload}}",
                "payload_function": "parseOrDecodeIncomingData",
                "payload_type": "source_payload",
                "resource": "uplink",
                "source": "resource",
                "update": "events"
              },
              "enabled": true,
              "retention": {
                "period": 12,
                "unit": "months"
              },
              "tags": [
                "energy",
                "lorawan"
              ]
            }
          },
          "code": {
            "code": "// Device Identifier Resolver configured in \"uplink\" API resource.\nfunction getId(payload) {\n    return payload.deviceId || payload.deviceEui || payload.dev_eui || 'unknown';\n}\n\n// Custom payload processing function configured in \"emu_emu_prof_ii_energy_data\" bucket.\nfunction parseOrDecodeIncomingData(payload) {\n    // Call the main decoder wrapper that handles both TTN v3 and Chirpstack formats\n    return decodeThingerUplink(payload);\n}\n\nfunction decodeThingerUplink(thingerData) {\n    // 0. If data has already been decoded, we will return it\n    if (thingerData.decodedPayload) return thingerData.decodedPayload;\n    \n    // 1. Extract and Validate Input\n    // We need 'payload' (hex string) and 'fPort' (integer)\n    const hexPayload = thingerData.payload || \"\";\n    const port = thingerData.fPort || 1;\n\n    // 2. Convert Hex String to Byte Array\n    const bytes = [];\n    for (let i = 0; i < hexPayload.length; i += 2) {\n        bytes.push(parseInt(hexPayload.substr(i, 2), 16));\n    }\n\n    // 3. Dynamic Function Detection and Execution\n    \n    // CASE A: (The Things Stack v3)\n    if (typeof decodeUplink === 'function') {\n        try {\n            const input = {\n                bytes: bytes,\n                fPort: port\n            };\n            var result = decodeUplink(input);\n            \n            if (result.data) return result.data;\n\n            return result; \n        } catch (e) {\n            console.error(\"Error inside decodeUplink:\", e);\n            throw e;\n        }\n    }\n\n    // CASE B: Legacy TTN (v2)\n    else if (typeof Decoder === 'function') {\n        try {\n            return Decoder(bytes, port);\n        } catch (e) {\n            console.error(\"Error inside Decoder:\", e);\n            throw e;\n        }\n    }\n\n    // CASE C: No decoder found\n    else {\n        throw new Error(\"No compatible TTN decoder function (decodeUplink or Decoder) found in scope.\");\n    }\n}\n\n\n// TTN decoder\n/**\n* MIT License\n* Copyright (c) 2021 EMU Electronic AG (https://www.emuag.ch/). All rights reserved.\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n/**\n * decodeUplink is called by TheThingsNetwork\n * we use our parsePayload() for decoding\n * \n * @param {*} input \n * @returns object containing decoded payload\n * {\n *      \"Active Energy Export T1\": {\n *        \"unit\": \"Wh\",\n *        \"value\": 0\n *      },\n *      \"Active Energy Export T2\": {\n *        \"unit\": \"Wh\",\n *        \"value\": 0\n *      },\n *      \"Active Energy Import T1\": {\n *        \"unit\": \"Wh\",\n *        \"value\": 4000\n *      },\n *      \"Active Energy Import T2\": {\n *        \"unit\": \"Wh\",\n *        \"value\": 0\n *      },\n *      \"Reactive Energy Export T1\": {\n *        \"unit\": \"varh\",\n *        \"value\": 0\n *      },\n *      \"Reactive Energy Export T2\": {\n *        \"unit\": \"varh\",\n *        \"value\": 0\n *      },\n *      \"Reactive Energy Import T1\": {\n *        \"unit\": \"varh\",\n *        \"value\": 0\n *      },\n *      \"Reactive Energy Import T2\": {\n *        \"unit\": \"varh\",\n *        \"value\": 0\n *      },\n *      \"medium\": {\n *        \"desc\": \"Electricity\",\n *        \"type\": 1\n *      },\n *      \"readoutInterval\": 900,\n *      \"timeStamp\": 1635499020,\n *      \"timestamp\": {\n *        \"unit\": \"seconds\",\n *        \"value\": 1635499020\n *      }\n */\nfunction decodeUplink(input) {\n    data = input.bytes;\n    //uplink with only 2 bytes is only status update, ignore it \n    if(data.length<=2){\n        return {};\n    }\n    \n    var obj = {};\n    \n    \n    //check CRC-8 which resides at the end\n    crc8Received = data[data.length - 1];\n    dataToCheck = [];\n    for(var i = 0; i < data.length - 1; i++){\n        dataToCheck.push(data[i]);\n    }\n    \n    if (crc8_encode(dataToCheck).toString(16) === crc8Received.toString(16)) {\n        //crc-8 seems ok, \n    } else {\n        obj.warnings = ['crc-8 wrong'];\n        //perhaps decide to stop further processing if crc-8 is wrong\n    } \n    \n    //first 4 bytes are allways the timestamp, this is the timestamp from the datalogger\n    var timeStamp = getUint32(data);\n    \n    obj.data = parsePayload(data);\n\n    //for TTN we strip unused information\n    for(var property in obj.data){\n        delete obj.data[property].cfgdescription;\n        delete obj.data[property].cfgtariff;\n        delete obj.data[property].cfgunit;\n        delete obj.data[property].cfgorder;\n        delete obj.data[property].order;\n        \n        \n        \n    }\n\n    obj.data.timeStamp = timeStamp;\n    obj.data.medium = {\n        \"type\": 1,\n        \"desc\": \"Electricity\"\n    };\n         \n    return obj;\n}\n\n/**\n * Decode is called by Chirpstack\n * @param {*} fPort \n * @param {*} data \n * @param {*} variables \n * @returns object containing decoded payload\n * \n * {\n *  \"Active Energy Import T1\": {\n *     \"unit\": \"Wh\",\n *     \"cfgdescription\": 3,\n *     \"cfgunit\": 1,\n *     \"cfgtariff\": 1,\n *     \"order\": 3,\n *     \"value\": 1809\n *  },\n *  \"Active Energy Import T2\": {\n *     \"unit\": \"Wh\",\n *     \"cfgdescription\": 3,\n *     \"cfgunit\": 1,\n *     \"cfgtariff\": 2,\n *     \"order\": 4,\n *     \"value\": 128\n *  },\n *  \"Active Energy Export T1\": {\n *     \"unit\": \"Wh\",\n *     \"cfgdescription\": 5,\n *     \"cfgunit\": 1,\n *     \"cfgtariff\": 1,\n *     \"order\": 5,\n *     \"value\": 1149\n *  },\n *  \"Active Energy Export T2\": {\n *     \"unit\": \"Wh\",\n *     \"cfgdescription\": 5,\n *     \"cfgunit\": 1,\n *     \"cfgtariff\": 2,\n *     \"order\": 6,\n *     \"value\": 17794\n *  },\n *  \"Reactive Energy Import T1\": {\n *     \"unit\": \"varh\",\n *     \"cfgdescription\": 10,\n *     \"cfgunit\": 5,\n *     \"cfgtariff\": 1,\n *     \"order\": 7,\n *     \"value\": 1864\n *  },\n *  \"Reactive Energy Import T2\": {\n *     \"unit\": \"varh\",\n *     \"cfgdescription\": 10,\n *     \"cfgunit\": 5,\n *     \"cfgtariff\": 2,\n *     \"order\": 8,\n *     \"value\": 2600\n *  },\n *  \"Reactive Energy Export T1\": {\n *     \"unit\": \"varh\",\n *     \"cfgdescription\": 13,\n *     \"cfgunit\": 5,\n *     \"cfgtariff\": 1,\n *     \"order\": 9,\n *     \"value\": 338\n *  },\n *  \"Reactive Energy Export T2\": {\n *     \"unit\": \"varh\",\n *     \"cfgdescription\": 13,\n *     \"cfgunit\": 5,\n *     \"cfgtariff\": 2,\n *     \"order\": 10,\n *     \"value\": 9661\n *  },\n *  \"timeStamp\": 1635499800,\n *  \"medium\": {\n *     \"type\": 1,\n *     \"desc\": \"Electricity\"\n *  },\n *  \"readoutInterval\": 900\n * }\n */\nfunction Decode(fPort, data, variables) {\n    //uplink with only 2 bytes is only status update, ignore it \n    if (data.length <= 2) {\n        return {};\n    }\n    \n    var obj = {};\n    \n    //check CRC-8 which resides at the end\n    crc8Received = data[data.length - 1];\n    dataToCheck = [];\n    for(var i = 0; i < data.length - 1; i++){\n        dataToCheck.push(data[i]);\n    }\n    \n    if (crc8_encode(dataToCheck).toString(16) === crc8Received.toString(16)) {\n        //crc-8 seems ok, \n    } else {\n        obj.warnings = ['crc-8 wrong'];\n        //perhaps decide to stop further processing if crc-8 is wrong\n    } \n    \n    //first 4 bytes are allways the timestamp, this is the timestamp from the datalogger\n    var timeStamp = getUint32(data);\n    obj = parsePayload(data);\n    obj.timeStamp = timeStamp;\n\n \n    //add a human readable timestamp to the payload\n    var meterDate = new Date(timeStamp * 1000);\n    var  options = { timeZone:'Europe/Berlin', weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour:'numeric', minute:'numeric', second:'numeric'};\n    obj.timeStampReadable = meterDate.toLocaleString('de-CH',options);\n\n    \n    obj.medium = {\n        \"type\": 1,\n        \"desc\": \"Electricity\"\n    };\n    \n    //Default readout-interval is allways 15 minutes\n    obj.readoutInterval = (15 * 60); //15 Min * 60 Sec\n    //you can overwrite the readout-interval when defining variables for this meter\n    if(variables !== null && variables.readoutInterval !== null){\n         obj.readoutInterval = variables.readoutInterval;\n    }\n    \n    return obj;\n}\n\n\n/**\n * encodeDownlink is called by TheThingsNetwork\n * @param {*} data Object containing configuration\n * @returns binary data \n */\nfunction encodeDownlink(data){\n/**\n * Example JSON-Object for Timestamp, Energy 0x03-0x0A \n * {\n *   \"fPort\": 1,\n *   \"timeInterval\": 15,\n *   \"sndAck\": true,\n *   \"startReJoin\": false,\n *   \"portIsActive\": true,\n *   \"values\": [\n *       1,\n *       3,\n *       4,\n *       5,\n *       6,\n *       7,\n *       8,\n *       9,\n *       10\n *   ]\n * } \n * \n */\n    //fPort must be defined else define it\n    if (data.data.fPort === null || data.data.fPort === undefined){\n        data.data.fPort = 1;\n    }\n    fPort = data.data.fPort;\n    bytes = [];\n\n    //just call the Encode function \n    bytes = Encode(fPort, data.data, {});\n         \n    return {fPort: fPort, bytes: bytes};\n}\n\n/**\n * decodeDownlink is used by TheThingsNetwork\n * @param {*} input  binary data containing configuration\n * @returns data object containing decoded configuration\n */\nfunction decodeDownlink(input) {\n    var data = {};\n    data.fPort = input.fPort;\n    if(input.bytes.length > 3){\n        var i = 0;\n        data.timeInterval = Number(getInt16([input.bytes[i++], input.bytes[i++]]));\n        configFlag = input.bytes[i++];\n        //sndAck activated ?\n        if (configFlag & 0x02) {\n            data.sndAck = true;\n        }\n        else {\n            data.sndAck = false;\n        }\n        //start a rejoin after receiving this uplink ?\n        if (configFlag & 0x04) {\n            data.startReJoin = true;\n        }\n        else {\n            data.startReJoin = false;\n        }\n        //is this uplink on this port activated ?\n        if (configFlag & 0x08) {\n            data.portIsActive = true;\n        }\n        else {\n            data.portIsActive = false;\n        }\n        data.values = []\n        for (i; i < input.bytes.length - 1; ++i) {\n            data.values.push(Number(getUint8(input.bytes[i])));\n        }\n    }\n    return data;\n}\n/**\n * Encode is called by Chirpstack\n * @param {*} fPort \n * @param {*} data Object containing configuration\n * @param {*} variables \n * @returns binary data\n */\nfunction Encode(fPort, data, variables) {\n/**\n * Example JSON-Object for Timestamp, Energy 0x03-0x0A \n * {\n *   \"fPort\": 1,\n *   \"timeInterval\": 15,\n *   \"sndAck\": true,\n *   \"startReJoin\": false,\n *   \"portIsActive\": true,\n *   \"values\": [\n *       1,\n *       3,\n *       4,\n *       5,\n *       6,\n *       7,\n *       8,\n *       9,\n *       10\n *   ]\n * } \n * \n */    \n    bytes = [];\n    \n    //make sure the time is valid and between 1 and 65535!\n    data.timeInterval = Math.min(data.timeInterval, 0xFFFF);\n    data.timeInterval = Math.max(data.timeInterval,1 );\n    \n    //push second byte to first position\n    bytes.push(data.timeInterval & 0X00FF);\n    //push first byte to second pposition\n    bytes.push(data.timeInterval >> 8);\n    \n    var configFlags = 0x00;\n    //Send Acknowledge for each Uplink back\n    if (data.sndAck) {\n        configFlags |= 0x02;\n    }\n    //Start Re-Join\n    if (data.startReJoin) {\n        configFlags |= 0x04;\n    }\n    //Enable this port so it sends data\n    if (data.portIsActive) {\n        configFlags |= 0x08;\n    }\n    \n    //Push the config flag on the stack\n    bytes.push(configFlags);\n    for(var i=0; i< data.values.length; i++) {\n        bytes.push(data.values[i]);\n    }\n    //apply crc-8\n    crc8 = crc8_encode(bytes);\n    bytes.push(crc8);\n    \n    return bytes;\n}\n\n\n \n\n/**\n* read 1 byte of data an convert it to an Uint8\n* @param {*} data \n* @returns \n*/\nfunction getUint8(data) {\n    var value = data >>> 0;\n    return value;\n    \n}\n\nfunction flip(n) {\n    var x = [];\n    n = Number(n);\n    //will work only for positive numbers\n    var single = n.toString(2).split(\"\");\n    for(var i = 0; i<single.length; i++){\n        x.push(single[i] == 1 ? 0 : 1);\n    }\n    \n    var tmp = x.join(\"\");\n    var y = (parseInt(tmp, 2) + 1) * -1;\n    return y;\n}\n\n\n/**\n* read 1 byte of data an convert it to an Int8\n* @param {*} data \n* @returns \n*/\nfunction getInt8(data) {\n    \n    if(data === 0){return 0;}\n    if(data >> 7 == 1){\n        return flip(data);\n    }\n    var value = data >>> 0;\n    return value;\n}\n/**\n* read 2 bytes of data an convert it to an Int16\n* @param {*} data \n* @returns \n*/\nfunction getInt16(data) {\n    \n    value = (data[1] << 8 | data[0]);\n    return value;\n    \n}\n/**\n* read 2 bytes of data an convert it to an Uint16\n* @param {*} data \n* @returns \n*/\nfunction getUint16(data) {\n    value = (data[1] << 8 | data[0]) >>> 0;\n    return value;\n    \n    \n}\n/**\n* read 4 bytes of data an convert it to an Int32\n* @param {*} data \n* @returns \n*/\nfunction getInt32(data) {\n    value = (data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]);\n    return value;\n    \n    \n    \n}\n/**\n* * read 4 bytes of data an convert it to an Uint32\n* @param {*} data \n* @returns \n*/\nfunction getUint32(data) {\n    \n    value = (data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]) >>> 0;\n    return value;\n}\n\n\n\n/**\n* read 8 bytes of data an convert it to an Int64\n* @param {*} data \n* @returns \n*/\nfunction getInt64(data) {\n    //JS can't handle bitwise operation with more than 32bit !\n    //so this won't work \n    //if Chirpstack will use another javascript engine we could use typearray's\n    var value = Number((data[7] << 56 | data[6] << 48 | data[5] << 40 | data[4] << 32 | data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]));\n    return value;\n    \n}\n/**\n* * read 8 bytes of data an convert it to an Uint32\n* @param {*} data \n* @returns \n*/\nfunction getUint64(data) {\n    //JS can't handle bitwise operation with more than 32bit !\n    //so this won't work \n    //if Chirpstack will use another javascript engine we could use typearray's\n    \n    value = Number((data[7] << 56 | data[6] << 48 | data[5] << 40 | data[4] << 32 | data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]) >>> 0);\n    return value;\n    \n}\n\nfunction getBCD(data) {\n    var bcd = \"\";\n    for(var i=0; i< data.length; i++) {\n        bcd = bcd + \"\" + data[i];\n        \n    }\n    \n    return bcd;\n}\n\n\nfunction getASCII(data) {\n    var ascii = \"\";\n    \n    for(var i=0; i< data.length; i++) {\n        entry = getUint8(data[i]);\n        if (entry != 0x00) {\n            ascii = ascii + String.fromCharCode(entry.toString());\n        }\n    }\n    return ascii;\n}\n/**\n* parses the variable data and returns an object\n* \n* a value is identified by its id\n* \n* @param {*} obj \n* @param {*} data \n*/\nfunction parsePayload(data){\n    var dataTypes = [];\n    \n    //be sure to fill the complete array\n    //if we receive an invalid datatype we skip the rest of the data \n    for (i = 0; i < 256; i++) {\n        dataTypes[i] = {'len': 255,'description': 'invalid data-type'};\n    }\n    \n    //the \"order\" is assigned according to the entry\ndataTypes[0x00]={'len':4,'description':'data-logger-index','dataType':'Uint32'};\ndataTypes[0x01]={'len':4,'description':'timestamp','dataType':'Uint32','unit':'seconds'};\ndataTypes[0x02]={'len':4,'description':'timestamp-previous','dataType':'Uint32','unit':'seconds'};\ndataTypes[0x03]={'len':4,'description':'ActiveEnergyImportT1','dataType':'Uint32','unit':'Wh','cfgdescription':3,\"cfgunit\":1,'cfgtariff':1};\ndataTypes[0x04]={'len':4,'description':'ActiveEnergyImportT2','dataType':'Uint32','unit':'Wh','cfgdescription':3,\"cfgunit\":1,'cfgtariff':2};\ndataTypes[0x05]={'len':4,'description':'ActiveEnergyExportT1','dataType':'Uint32','unit':'Wh','cfgdescription':5,\"cfgunit\":1,'cfgtariff':1};\ndataTypes[0x06]={'len':4,'description':'ActiveEnergyExportT2','dataType':'Uint32','unit':'Wh','cfgdescription':5,\"cfgunit\":1,'cfgtariff':2};\ndataTypes[0x07]={'len':4,'description':'ReactiveEnergyImportT1','dataType':'Uint32','unit':'varh','cfgdescription':10,\"cfgunit\":5,'cfgtariff':1};\ndataTypes[0x08]={'len':4,'description':'ReactiveEnergyImportT2','dataType':'Uint32','unit':'varh','cfgdescription':10,\"cfgunit\":5,'cfgtariff':2};\ndataTypes[0x09]={'len':4,'description':'ReactiveEnergyExportT1','dataType':'Uint32','unit':'varh','cfgdescription':13,\"cfgunit\":5,'cfgtariff':1};\ndataTypes[0x0A]={'len':4,'description':'ReactiveEnergyExportT2','dataType':'Uint32','unit':'varh','cfgdescription':13,\"cfgunit\":5,'cfgtariff':2};\ndataTypes[0x0B]={'len':4,'description':'ActivePowerL123','dataType':'Int32','unit':'W','cfgdescription':25,\"cfgunit\":13};\ndataTypes[0x0C]={'len':4,'description':'ActivePowerL1','dataType':'Int32','unit':'W','cfgdescription':25,\"cfgunit\":13,'cfgphase':1};\ndataTypes[0x0D]={'len':4,'description':'ActivePowerL2','dataType':'Int32','unit':'W','cfgdescription':25,\"cfgunit\":13,'cfgphase':2};\ndataTypes[0x0E]={'len':4,'description':'ActivePowerL3','dataType':'Int32','unit':'W','cfgdescription':25,\"cfgunit\":13,'cfgphase':3};\ndataTypes[0x0F]={'len':4,'description':'CurrentL123','dataType':'Int32','unit':'mA','cfgdescription':31,\"cfgunit\":28};\ndataTypes[0x10]={'len':4,'description':'CurrentL1','dataType':'Int32','unit':'mA','cfgdescription':31,\"cfgunit\":28,'cfgphase':1};\ndataTypes[0x11]={'len':4,'description':'CurrentL2','dataType':'Int32','unit':'mA','cfgdescription':31,\"cfgunit\":28,'cfgphase':2};\ndataTypes[0x12]={'len':4,'description':'CurrentL3','dataType':'Int32','unit':'mA','cfgdescription':31,\"cfgunit\":28,'cfgphase':3};\ndataTypes[0x13]={'len':4,'description':'CurrentN','dataType':'Int32','unit':'mA','cfgdescription':31,\"cfgunit\":28,'cfgphase':4};\ndataTypes[0x14]={'len':4,'description':'VoltageL1-N','dataType':'Int32','unit':'V/10','unit_calculated':'V','factor':0.1,'fixed':1,'cfgdescription':30,\"cfgunit\":26,'cfgphase':1};\ndataTypes[0x15]={'len':4,'description':'VoltageL2-N','dataType':'Int32','unit':'V/10','unit_calculated':'V','factor':0.1,'fixed':1,'cfgdescription':30,\"cfgunit\":26,'cfgphase':2};\ndataTypes[0x16]={'len':4,'description':'VoltageL3-N','dataType':'Int32','unit':'V/10','unit_calculated':'V','factor':0.1,'fixed':1,'cfgdescription':30,\"cfgunit\":26,'cfgphase':3};\ndataTypes[0x17]={'len':1,'description':'PowerfactorL1','dataType':'Int8','unit':'Cos','factor':0.01,'fixed':2,'cfgdescription':32,\"cfgunit\":31,'cfgphase':1};\ndataTypes[0x18]={'len':1,'description':'PowerfactorL2','dataType':'Int8','unit':'Cos','factor':0.01,'fixed':2,'cfgdescription':32,\"cfgunit\":31,'cfgphase':2};\ndataTypes[0x19]={'len':1,'description':'PowerfactorL3','dataType':'Int8','unit':'Cos','factor':0.01,'fixed':2,'cfgdescription':32,\"cfgunit\":31,'cfgphase':3};\ndataTypes[0x1A]={'len':2,'description':'Frequency','dataType':'Int16','unit':'Hz','factor':0.1,'fixed':1,'cfgdescription':33,\"cfgunit\":32};\ndataTypes[0x1B]={'len':4,'description':'ActivePoweraverage','dataType':'Int32','unit':'W',\"cfgunit\":13,};\ndataTypes[0x1C]={'len':4,'description':'ActiveEnergyImportT1kWh','dataType':'Uint32','unit':'kWh','cfgdescription':3,\"cfgunit\":2,'cfgtariff':1};\ndataTypes[0x1D]={'len':4,'description':'ActiveEnergyImportT2kWh','dataType':'Uint32','unit':'kWh','cfgdescription':3,\"cfgunit\":2,'cfgtariff':2};\ndataTypes[0x1E]={'len':4,'description':'ActiveEnergyExportT1kWh','dataType':'Uint32','unit':'kWh','cfgdescription':5,\"cfgunit\":2,'cfgtariff':1};\ndataTypes[0x1F]={'len':4,'description':'ActiveEnergyExportT2kWh','dataType':'Uint32','unit':'kWh','cfgdescription':5,\"cfgunit\":2,'cfgtariff':2};\ndataTypes[0x20]={'len':4,'description':'ReactiveEnergyImportT1kvarh','dataType':'Uint32','unit':'kvarh','cfgdescription':10,\"cfgunit\":6,'cfgtariff':1};\ndataTypes[0x21]={'len':4,'description':'ReactiveEnergyImportT2kvarh','dataType':'Uint32','unit':'kvarh','cfgdescription':10,\"cfgunit\":6,'cfgtariff':2};\ndataTypes[0x22]={'len':4,'description':'ReactiveEnergyExportT1kvarh','dataType':'Uint32','unit':'kvarh','cfgdescription':13,\"cfgunit\":6,'cfgtariff':1};\ndataTypes[0x23]={'len':4,'description':'ReactiveEnergyExportT2kvarh','dataType':'Uint32','unit':'kvarh','cfgdescription':13,\"cfgunit\":6,'cfgtariff':2};\ndataTypes[0x24]={'len':8,'description':'ActiveEnergyImportT164bit','dataType':'uInt64','unit':'Wh','cfgdescription':3,\"cfgunit\":1,'cfgtariff':1};\ndataTypes[0x25]={'len':8,'description':'ActiveEnergyImportT264bit','dataType':'uInt64','unit':'Wh','cfgdescription':3,\"cfgunit\":1,'cfgtariff':2};\ndataTypes[0x26]={'len':8,'description':'ActiveEnergyExportT164bit','dataType':'uInt64','unit':'Wh','cfgdescription':5,\"cfgunit\":1,'cfgtariff':1};\ndataTypes[0x27]={'len':8,'description':'ActiveEnergyExportT264bit','dataType':'uInt64','unit':'Wh','cfgdescription':5,\"cfgunit\":1,'cfgtariff':2};\ndataTypes[0x28]={'len':8,'description':'ReactiveEnergyImportT164bit','dataType':'uInt64','unit':'varh','cfgdescription':10,\"cfgunit\":5,'cfgtariff':1};\ndataTypes[0x29]={'len':8,'description':'ReactiveEnergyImportT264bit','dataType':'uInt64','unit':'varh','cfgdescription':10,\"cfgunit\":5,'cfgtariff':2};\ndataTypes[0x2A]={'len':8,'description':'ReactiveEnergyExportT164bit','dataType':'uInt64','unit':'varh','cfgdescription':13,\"cfgunit\":5,'cfgtariff':1};\ndataTypes[0x2B]={'len':8,'description':'ReactiveEnergyExportT264bit','dataType':'uInt64','unit':'varh','cfgdescription':13,\"cfgunit\":5,'cfgtariff':2};\ndataTypes[0xF0]={'len':1,'description':'errorcode','dataType':'ErrorCode'};\ndataTypes[0xF1]={'len':4,'description':'serial-number','dataType':'MeterSerial'};\ndataTypes[0xF2]={'len':4,'description':'factor-number','dataType':'MeterSerial'};\ndataTypes[0xF3]={'len':2,'description':'current-transformerprimary','dataType':'Uint16',\"cfgunit\":72,};\ndataTypes[0xF4]={'len':2,'description':'current-transformersecondary','dataType':'Uint16',\"cfgunit\":72,};\ndataTypes[0xF5]={'len':2,'description':'voltage-transformerprimary','dataType':'Uint16',\"cfgunit\":72,};\ndataTypes[0xF6]={'len':2,'description':'voltage-transformersecondary','dataType':'Uint16',\"cfgunit\":72,};\ndataTypes[0xF7]={'len':1,'description':'meter-typ','dataType':'Uint8'};\ndataTypes[0xF8]={'len':4,'description':'MIDyear','dataType':'BCD',};\ndataTypes[0xF9]={'len':4,'description':'factoryyear','dataType':'BCD',};\ndataTypes[0xFA]={'len':4,'description':'firmwareversion','dataType':'ASCII'};\ndataTypes[0xFB]={'len':4,'description':'mid-Version','dataType':'ASCII'};\ndataTypes[0xFC]={'len':4,'description':'manufacturer','dataType':'ASCII'};\ndataTypes[0xFD]={'len':4,'description':'hw-index','dataType':'ASCII'};\ndataTypes[0xFE]={'len':4,'description':'systemtime','dataType':'Uint32'};\n    \n    \n    var obj = {};\n    \n    var i = 4; //the first 4 bytes is allways the timestamp\n    //the last byte is the crc-code so ignore this one\n    while (i < (data.length - 1)) {\n        //extract signature byte \n        indexOfDataType = data[i];\n        dataType = dataTypes[indexOfDataType];\n        i++;\n        //also save the sort-order value  \n        dataType.order = indexOfDataType;\n        \n        switch (dataType.dataType) {\n            case 'Int8':\n            dataType.value = Number(getInt8([data[i++]]));\n            break;\n            case 'Uint8':\n            dataType.value = Number(getUint8([data[i++]]));\n            break;\n            case 'Int16':\n            dataType.value = Number(getInt16([data[i++], data[i++]]));\n            break;\n            case 'Uint16':\n            dataType.value = Number(getUint16([data[i++], data[i++]]));\n            break;\n            case 'Uint32':\n            dataType.value = Number(getUint32([data[i++], data[i++], data[i++], data[i++]]));\n            break;\n            case 'Int32':\n            dataType.value = Number(getInt32([data[i++], data[i++], data[i++], data[i++]]));\n            break;\n            case 'uInt64':\n            dataType.value = Number(getUint64([data[i++], data[i++], data[i++], data[i++], data[i++], data[i++], data[i++], data[i++]]));\n            break;\n            case 'Int64':\n            dataType.value = Number(getInt64([data[i++], data[i++], data[i++], data[i++], data[i++], data[i++], data[i++], data[i++]]));\n            break;\n            case 'MeterSerial':\n\n                dataType.value = ('0' + Number(getUint8([data[i++]])).toString(16)).slice(-2);\n                dataType.value = ('0' + Number(getUint8([data[i++]])).toString(16)).slice(-2) + dataType.value;\n                dataType.value = ('0' + Number(getUint8([data[i++]])).toString(16)).slice(-2) + dataType.value;\n                dataType.value = ('0' + Number(getUint8([data[i++]])).toString(16)).slice(-2) + dataType.value;\n\n            break;\n            case 'BCD':\n            dataType.value = getBCD([data[i++], data[i++], data[i++], data[i++]]);\n            break;\n            case 'ASCII':\n            dataType.value = getASCII([data[i++], data[i++], data[i++], data[i++]]);\n            break;\n            case 'ErrorCode':\n            dataType.value =  Number(getUint8([data[i++]]));\n            //also encode the error\n            dataType.TimeChanged =  dataType.value & 0x01 ? true : false;\n            dataType.CTRatioChange =  dataType.value & 0x02 ? true : false;\n            dataType.VTRatioChange =  dataType.value & 0x04 ? true : false;\n            dataType.ImpulseWidthChange =  dataType.value & 0x08 ? true : false;\n            dataType.ImpulseRatioChange =  dataType.value & 0x10 ? true : false;\n            dataType.PowerFail =  dataType.value & 0x20 ? true : false;\n            dataType.LogbookFull = dataType.value & 0x80 ? true : false;\n            \n            break;\n            default:\n            break;\n            \n        }\n        //if we have a factor apply it but keep the old value\n        if (dataType.factor && !isNaN(dataType.factor)) {\n            var fixed = 0;\n            if (dataType.fixed && !isNaN(dataType.fixed)) {\n                fixed = dataType.fixed;\n            }\n            //save the value which was sent by the meter (perhaps needed later)\n            dataType.value_raw = dataType.value;\n            //calculate the new value using the factor\n            dataType.value = Number((dataType.value * dataType.factor).toFixed(fixed));\n        }\n        \n        obj[dataType.description] = dataType;\n        //remove all unused infos like dataType, description, len\n        delete dataType.len;\n        delete dataType.description;\n        delete dataType.dataType;\n        delete dataType.factor;\n        delete dataType.fixed;\n    }\n    \n    return obj;\n    \n    \n}\nfunction crc8_encode(data) {\n    \n    var xorOut = 0x0000;\n    var table = [\n        0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B,\n        0x12, 0x15, 0x38, 0x3F, 0x36, 0x31,\n        0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77,\n        0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,\n        0x48, 0x4F, 0x46, 0x41, 0x54, 0x53,\n        0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9,\n        0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF,\n        0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,\n        0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B,\n        0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,\n        0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0,\n        0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,\n        0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4,\n        0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,\n        0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88,\n        0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,\n        0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C,\n        0x35, 0x32, 0x1F, 0x18, 0x11, 0x16,\n        0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50,\n        0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,\n        0x6F, 0x68, 0x61, 0x66, 0x73, 0x74,\n        0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80,\n        0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6,\n        0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,\n        0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2,\n        0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8,\n        0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E,\n        0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,\n        0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A,\n        0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,\n        0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26,\n        0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,\n        0x4E, 0x49, 0x40, 0x47, 0x52, 0x55,\n        0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F,\n        0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39,\n        0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,\n        0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D,\n        0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,\n        0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91,\n        0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,\n        0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5,\n        0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF,\n        0xFA, 0xFD, 0xF4, 0xF3\n    ];\n    var crc = 0x0000;\n    for (var j = 0; j < data.length; j++) {\n        crc = table[crc ^ data[j]];\n    }\n    return (crc ^ xorOut) & 0xFFFF;\n    \n}\n",
            "environment": "javascript",
            "storage": "",
            "version": "1.0"
          },
          "properties": {
            "uplink": {
              "data": {
                "payload": "{{payload}}",
                "payload_function": "",
                "payload_type": "source_payload",
                "resource": "uplink",
                "source": "resource",
                "update": "events"
              },
              "default": {
                "source": "value"
              },
              "enabled": true
            }
          }
        },
        "_resources": {
          "properties": [
            {
              "property": "dashboard",
              "value": {
                "tabs": [
                  {
                    "name": "Energy Monitoring",
                    "widgets": [
                      {
                        "layout": {
                          "col": 0,
                          "row": 0,
                          "sizeX": 2,
                          "sizeY": 6
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "Active Power (Total)"
                        },
                        "properties": {
                          "color": "#1abc9c",
                          "max": 10000,
                          "min": -10000,
                          "unit": "W"
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActivePowerL123.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#1abc9c",
                            "name": "Active Power",
                            "source": "bucket",
                            "timespan": {
                              "mode": "latest"
                            }
                          }
                        ],
                        "type": "donutchart"
                      },
                      {
                        "layout": {
                          "col": 2,
                          "row": 0,
                          "sizeX": 2,
                          "sizeY": 6
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "Frequency"
                        },
                        "properties": {
                          "color": "#e74c3c",
                          "max": 55,
                          "min": 45,
                          "unit": "Hz"
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "Frequency.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#e74c3c",
                            "name": "Frequency",
                            "source": "bucket",
                            "timespan": {
                              "mode": "latest"
                            }
                          }
                        ],
                        "type": "donutchart"
                      },
                      {
                        "layout": {
                          "col": 0,
                          "row": 6,
                          "sizeX": 4,
                          "sizeY": 12
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "Active Energy Import/Export (T1)"
                        },
                        "properties": {
                          "axis": true,
                          "fill": false,
                          "legend": true,
                          "multiple_axes": false
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActiveEnergyImportT1.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#27ae60",
                            "name": "Import T1",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "day",
                              "mode": "relative",
                              "period": "latest",
                              "value": 7
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActiveEnergyExportT1.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#e67e22",
                            "name": "Export T1",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "day",
                              "mode": "relative",
                              "period": "latest",
                              "value": 7
                            }
                          }
                        ],
                        "type": "chart"
                      },
                      {
                        "layout": {
                          "col": 4,
                          "row": 0,
                          "sizeX": 2,
                          "sizeY": 18
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "3-Phase Active Power"
                        },
                        "properties": {
                          "axis": true,
                          "fill": false,
                          "legend": true,
                          "multiple_axes": false
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActivePowerL1.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#e74c3c",
                            "name": "Phase L1",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActivePowerL2.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#f39c12",
                            "name": "Phase L2",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "ActivePowerL3.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#3498db",
                            "name": "Phase L3",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          }
                        ],
                        "type": "chart"
                      },
                      {
                        "layout": {
                          "col": 0,
                          "row": 18,
                          "sizeX": 3,
                          "sizeY": 12
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "3-Phase Voltage (L-N)"
                        },
                        "properties": {
                          "axis": true,
                          "fill": false,
                          "legend": true,
                          "multiple_axes": false
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "VoltageL1-N.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#e74c3c",
                            "name": "L1-N",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "VoltageL2-N.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#f39c12",
                            "name": "L2-N",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "VoltageL3-N.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#3498db",
                            "name": "L3-N",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          }
                        ],
                        "type": "chart"
                      },
                      {
                        "layout": {
                          "col": 3,
                          "row": 18,
                          "sizeX": 3,
                          "sizeY": 12
                        },
                        "panel": {
                          "color": "#ffffff",
                          "currentColor": "#ffffff",
                          "showOffline": {
                            "type": "none"
                          },
                          "title": "3-Phase Current"
                        },
                        "properties": {
                          "axis": true,
                          "fill": false,
                          "legend": true,
                          "multiple_axes": false
                        },
                        "sources": [
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "CurrentL1.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#e74c3c",
                            "name": "L1",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "CurrentL2.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#f39c12",
                            "name": "L2",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          },
                          {
                            "bucket": {
                              "backend": "mongodb",
                              "id": "emu_emu_prof_ii_energy_data",
                              "mapping": "CurrentL3.value",
                              "tags": {
                                "device": [],
                                "group": []
                              }
                            },
                            "color": "#3498db",
                            "name": "L3",
                            "source": "bucket",
                            "timespan": {
                              "magnitude": "hour",
                              "mode": "relative",
                              "period": "latest",
                              "value": 24
                            }
                          }
                        ],
                        "type": "chart"
                      }
                    ]
                  }
                ]
              }
            }
          ]
        }
      }
    ]
  }
}