ZENNER EASY PROTECT Radio decoder

Good morning,
I am looking for a TTN decoder for the ZENNER EASY PROTECT radio.
Unfortunately I am not so well versed that I can create this asu the instructions myself.
I’ve found 2 on the net, but unfortunately they don’t work for me.
Does anyone have this in operation and a working decoder to hand that I can use?
Thanks

I use this one:

/*

  • studierendenwerk-ulm/payload-decoder-zenner-easy-protect-radio-lora/master/decoder.js
    */

/**

  • Main decoder function

  • @param bytes

  • @param port

  • @constructor
    /
    function Decoder(bytes, port) {
    if(bytes === null || bytes === 0){
    //obj.port = port;
    obj.status_dedcoded = false;
    obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
    } else {
    // Definition of head line daily value.
    /
    let S_SMOKE_CHAMBER_POLLUTION_PREWARNING = 0x0800;
    let S_SMOKE_CHAMBER_POLLUTION_WARNING = 0x1000;
    let S_TEST_BUTTON_FAILUER = 0x2000;
    let S_ACCUSTIC_ALARM_FAILUER = 0x4000;
    let S_REMOVAL_DETECTION = 0x8000;
    let S_TEST_ALARM = 0x0001;
    let S_SMOKE_ALARM = 0x0002;
    let S_OBSTRUCTION_DETECTION = 0x0004;
    let S_SURROUNDING_AREA_MONITORING = 0x0008;
    let S_LED_FAILURE = 0x0010;*/

     // Definition of the different packet types and subtypes.
     var TYPE_SP1 = 0x1; //sent daily -> packet, max. 2 retransmissions
     var TYPE_SP9 = 0x9; // Sent immediately after first  activation and from then on every 6 months -> No retransmissions
     var SUBTYPE_SP9_1 = 0x01; // sent every month except the month of first activation -> No retransmissions
     var SUBTYPE_SP9_2 = 0x02; // sent immediately after first activation and from then on every 6 months -> No retransmissions
     var TYPE_AP1 = 0xA; //  (status code, status data): event based -> Max 5 AP packets per month, no retransmissions
    
     // Device specific status summery definition
     var A_REMOVAL = 0x02;
     var A_BATTERY_END_OF_LIFE = 0x0C;
     var A_HORN_DRIVE_LEVEL_FAILURE = 0x16;
     var A_OBSTRUCTION_DETECTION = 0x1A;
     var A_OBJECT_IN_THE_SURROUNDING_AREA = 0x1C;
     var AP_VALUES = ["removal", "battery end of life",
         "horn drive level failure", "obstruction detection", "object in the surrounding area"];
     var S_STATUS_SUMMERY_VALUES = ["removal", "0",
         "battery end of life", "acoustic alarm failure",
         "obstruction detection", "surrounding area monitoring"];
    
     var obj = {};
     obj.port = port;
    
     switch (bytes[0] >> 4) {
         case TYPE_SP1:
             //const casePosition = "SP1";
             obj.packet_type = 1;
             obj.packet_subtype = 0;
             obj.packet_type_info = "synchronous";
             //obj.status_interpretation = null;
             obj.status_dedcoded = false;
             obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
             //obj.decodingTree = [casePosition];
             break;
         case TYPE_SP9:
             obj.packet_type = 9;
             obj.packet_type_info = "synchronous";
             obj.status_dedcoded = false;
             // check for packet subtype SP9.1 and SP9.2
             switch (bytes[0] & 0x0F) {
                 case SUBTYPE_SP9_1:
                     obj.packet_subtype = 1;
                     obj.dateTime = decodeDateAndTime(((bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | (bytes[4])));
                     //TODO make a function to check is the device in correct dateAndTime
                     obj.status_interpretation = buildStatusSummery(bytes[5], bytes[6], S_STATUS_SUMMERY_VALUES);
                     obj.status_dedcoded = true;
                     obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
                     break;
                 case SUBTYPE_SP9_2:
                     obj.packet_subtype = 2;
                     obj.status_interpretation = [
                         "firmware version: " + (
                             ((bytes[1] << 24) + (bytes[2] << 16) + (bytes[3] << 8) + (bytes[4]))
                                 .toString(16)).toUpperCase(),
                         "LoRa WAN version: " + (bytes[5]).toString(16) + "." + (bytes[6])
                             .toString(16) + "." + (bytes[7]).toString(16),
                         "LoRa command version: " + (bytes[9]).toString(16) + "." + (bytes[8]).toString(16),
                         "device type: " + (bytes[10]).toString(16),
                         "meter ID: " + (((bytes[11] << 24) + (bytes[12] << 16) + (bytes[13] << 8) + (bytes[14]))
                             .toString(16)).toUpperCase()
                     ];
                     obj.status_dedcoded = true;
                     obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
                     break;
                 default:
                     obj.status_dedcoded = false;
                     obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
                     break;
             }
             break;
         case TYPE_AP1:
             obj.packet_type = 1;
             obj.packet_subtype = 0;
             obj.packet_type_info = "asynchronous";
             obj.date = (decodeDate(bytes [3] << 8 | bytes[4]));
             obj.status_interpretation = null;
             obj.status_dedcoded = false;
             obj.status_info = null;
             obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
    
             switch (bytes[1]) {
                 case A_REMOVAL:
                     obj.status_interpretation = AP_VALUES[0];
                     obj.status_dedcoded = true;
                     break;
                 case A_BATTERY_END_OF_LIFE:
                     obj.status_interpretation = AP_VALUES[1];
                     obj.status_dedcoded = true;
                     break;
                 case A_HORN_DRIVE_LEVEL_FAILURE:
                     obj.status_interpretation = AP_VALUES[2];
                     obj.status_dedcoded = true;
                     break;
                 case A_OBSTRUCTION_DETECTION:
                     obj.status_interpretation = AP_VALUES[3];
                     obj.status_dedcoded = true;
                     break;
                 case A_OBJECT_IN_THE_SURROUNDING_AREA:
                     obj.status_interpretation = AP_VALUES[4];
                     obj.status_dedcoded = true;
                     break;
                 default:
                     obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
                     break;
             }
             break;
         default:
             obj.status_dedcoded = false;
             obj.status_info = "payload_raw: " + payloadRawHexToString(bytes);
             break;
     }
    

    }
    return obj;
    }

/**

  • decodes date (EN13757-3:2013, Annex A, data type G).
  • input: int, lower two bytes interpreted.
  • output: date string YYYY-MM-DD.
  • @param bytes
  • @returns {string}
    */
    function decodeDate(bytes) {
    //TODO Handling errors with 0xFF see LoRa radio packet definitions page 15
    var day = (bytes & 0x1F00) >> 8;
    var month = (bytes & 0x000F);
    var year = ((bytes & 0xE000) >> 10) | ((bytes & 0x00F0) >> 4);
    return “20” + year.toString() + “-” + month.toString() + “-” + day.toString();
    }

/**

  • Date and time format.
  • Date&Time stamp coding according to EN13757-3:2013, Annex A, data type F
  • @param bytes
  • @returns {string}
    */
    function decodeDateAndTime(bytes) {
    var minute = (bytes & 0x3F000000) >> 24;
    var hour = (bytes & 0x001F0000) >> 16;
    var day = (bytes & 0x00001F00) >> 8;
    var month = (bytes & 0x0000000F);
    var year = ((bytes & 0x0000E000) >> 10) | ((bytes & 0x000000F0) >> 4);
    return “20” + year.toString() + “-”
    + month.toString() + “-”
    + day.toString() + “T”
    + hour.toString() + “:”
    + minute.toString()
    + “:00Z”;
    }

/**
*

  • @param byteArray
  • @returns {string}
    */
    function payloadRawHexToString(byteArray) {
    var str;
    if(byteArray === null){
    return “00”;
    }else {
    str = byteArray.map(function (byte) {
    return (‘0’ + (byte & 0xFF).toString(16)).slice(-2);
    }).join(‘’);
    var strArray = str.match(/.{2}/g);
    str = strArray.map(function (value, index, array) {
    return array[index];
    });
    var x = str.join();
    return x.replace(/([,])/g, " ").toLocaleUpperCase();
    }
    }

/**

  • Build the summary for package 9.1.

  • @param a

  • @param b

  • @param S_STATUS_SUMMERY_VALUES

  • @returns {}
    */
    function buildStatusSummery(a, b,S_STATUS_SUMMERY_VALUES) {

    var bin1 = parseInt(a.toString(), 16).toString(2);
    var bin2 = parseInt(b.toString(), 16).toString(2);
    var result = ;

    for (var i = 0; i < bin1.length; i++) {
    console.log(bin1.length);
    if (bin1[i] === “1”) {
    result.push(S_STATUS_SUMMERY_VALUES[i]);
    }
    }

    for (var j = 0; j < bin2.length; j++) {
    if (bin2[i] === “1”) {
    result.push(S_STATUS_SUMMERY_VALUES[j]);
    }
    }

    return result;
    }

But I do not get the data correcly into Datacake…as I do not have the knowledge of java, databases and decoding the info

Can you please the complete payload in code.
I can not copy it.
THX

Hi ,
how do I do that…?
Marc

Write in a File und Upload here.
This is the best way.

Hi HSN-TTN

attached the file
Zenner.txt (9.1 KB)

Let me know if you can download the file

Best
Marc

Hello,
Unfortunately I only got around to testing now.
But the payload only brings me:
Decode uplink data message failure

Just an update on this old thread, my ZENNER devices seem to work fine with TTN and I just share my experience here: