//++++++VERSION 1.2++++++15.04.2026
// Decode uplink function.
  //
  // Input is an object with the following fields:
  // - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
  // - fPort = Uplink fPort.
  // - recvTime = Uplink message timestamp as Date object.
  // - variables = Object containing the configured device variables.
  //
  // Output must be an object with the following fields:
  // - data = Object representing the decoded payload.
function decodeUplink(input) {
  var index = 4;
  var x=0;		

    switch (input.fPort) {
        case 1:
            return {
                data: {
                    ZS: 			parse4BytesMsb(input.bytes, 0),
                    STYZS: 			parse4BytesMsb(input.bytes, 4),
                  	STMZS: 			parse4BytesMsb(input.bytes, 8),
				  	Month_Last: 	input.bytes[12]>>4,
				  	Year_Month: 	input.bytes[12] & 0x0F,
                    STATUS: 		parseStatusBits(input.bytes,13)
                }
            };
		case 2:
		    //Temperaturen der letzten Stunde aller 4min
		    index = 4;
            return {
              data: {
                		ZEIT: 				parse4BytesToTime(input.bytes, 0),
                		Temp0:				input.bytes[index++],
						Temp1:				input.bytes[index++],
                		Temp2:				input.bytes[index++],
                		Temp3:				input.bytes[index++],
                		Temp4:				input.bytes[index++],
						Temp5:				input.bytes[index++],
						Temp6:				input.bytes[index++],
                		Temp7:				input.bytes[index++],
                		Temp8:				input.bytes[index++],                
                		Temp9:				input.bytes[index++],                
                		Temp10:				input.bytes[index++],                
                		Temp11:				input.bytes[index++],                
                		Temp12:				input.bytes[index++],
                		Temp13:				input.bytes[index++],
                		Temp14:				input.bytes[index++],
                		STATUS: 			parseStatusBits(input.bytes,index)
					}
            };
            
      case 4:
          //history mit Durchschnittstemperaturen der letzten 24h
			index = 17;
            return {
                data: {
                    ZS: 			parse4BytesMsb(input.bytes, 0),
                    STYZS: 			parse4BytesMsb(input.bytes, 4),
                  	STMZS: 			parse4BytesMsb(input.bytes, 8),
				  	Month_Last: 	input.bytes[12]>>4,
                    Year_Month: 	input.bytes[12] & 0x0F,
                    ZEIT: 			parse4BytesToTime(input.bytes, 13),
                  	DST1h:			input.bytes[index++],
                  	DST2h:			input.bytes[index++],
                  	DST3h:			input.bytes[index++],
					DST4h:			input.bytes[index++],
                    DST5h:			input.bytes[index++],
                    DST6h:			input.bytes[index++],
                    DST7h:			input.bytes[index++],
                    DST8h:			input.bytes[index++],
                    DST9h:			input.bytes[index++],
                    DST10h:			input.bytes[index++],
                    DST11h:			input.bytes[index++],
                    DST12h:			input.bytes[index++],
                    DST13h:			input.bytes[index++],
                    DST14h:			input.bytes[index++],
                    DST15h:			input.bytes[index++],
                    DST16h:			input.bytes[index++],
                    DST17h:			input.bytes[index++],
                    DST18h:			input.bytes[index++],
                    DST19h:			input.bytes[index++],
                    DST20h:			input.bytes[index++],
                    DST21h:			input.bytes[index++],
                    DST22h:			input.bytes[index++],
                    DST23h:			input.bytes[index++],
                    DST24h:			input.bytes[index++],
                    STATUS: 		parseStatusBits(input.bytes,index)
               		 }
            };
	case 5:
	        //12 Monatswerte
	        index=0;
            return {
                data: {
                        Month: 		input.bytes[0],
				  		YEAR:		input.bytes[1]+2000,
                  		ZSM: 		parse2BytesMSBasArray(input.bytes,2,12)
                	}
            };
	case 6:
	        //12 Halbmonatswerte 
            return {
                data: {
                        Month: 		input.bytes[0],
				  		YEAR:		input.bytes[1]+2000,
                  		ZSHM: 		parse2BytesMSBasArray(input.bytes,2,12)
                	}
            };

        case 9:
            return {
                data: {
                    	SF7: 			parse4BytesLsb(input.bytes, 0),
                    	SF8: 			parse4BytesLsb(input.bytes, 4),
                    	SF9: 			parse4BytesLsb(input.bytes, 8),
                    	SF10: 			parse4BytesLsb(input.bytes, 12),
                    	SF11: 			parse4BytesLsb(input.bytes, 16),
                  		SF12: 			parse4BytesLsb(input.bytes, 20),
                  		JOINCOUNTER: 	parse2BytesMsb(input.bytes,24),
					 	VDD_mV :		parse2BytesMsb(input.bytes,26)
						//STATUS: 		parseStatusBits(input.bytes,27)
                	}
            };
        case 11:
            //Installtelegramm
        return {
				data: {
    	                ZS: 			parse4BytesMsb(input.bytes, 0),
    	                CONFIG:         parseInstallBits(input.bytes, 4),
    	                STATUS: 		parseStatusBits(input.bytes,23)
    	            }
                };

    }
    return {
        data: {
        }
    }
}

function parseInstallBits(bytes, start_index) {
    var msblsb = (bytes[start_index+17]<<8);
    msblsb |= bytes[start_index+18];
  	var intervall = (msblsb >> 5) & 7;
  	var confirm = (msblsb >> 14) & 3;
    return {
    	                HW_VERSION: 	bytesToBCD_1(bytes[start_index+0]), 
    	                SW_VERSION: 	bytesToBCD_1(bytes[start_index+1]),
    	                GRNr: 			bytesToBCD_4([bytes[start_index+5],bytes[start_index+4],bytes[start_index+3],bytes[start_index+2]]),
    	                ChipTemp: 		bytes[start_index+6],
		                VDD_mV:		    parse2BytesMsb(bytes,7+start_index),
      	                K1:           	parse2BytesMsb(bytes,9+start_index),
      	                K2: 		    parse2BytesMsb(bytes,11+start_index),
      	                KQ: 	        parse2BytesMsb(bytes,13+start_index),
      	                Batt:           bytes[start_index+15],
      	                ForceReJoin: 	bytes[start_index+16],

                        CONFIRM: 	    confirm === 0 ? "DIS" :
    					                confirm === 1 ? "ALL" :
    					                confirm === 2 ? "1TEL" :
						                confirm === 3 ? "5TEL" : "DISABLE",
                        OPTION0:    bytes[start_index+17], 
                        OPTION1:    bytes[start_index+18], 
                        OTAA: 		Boolean(msblsb & (1 << 13)),
                        ADR: 		Boolean(msblsb & (1 << 12)),
                        LINKCHECK: 	Boolean(msblsb & (1 << 11)),
                        RTC_AUTO: 	Boolean(msblsb & (1 << 10)),
                        ESCALA:		Boolean(msblsb & (1 << 9)),
                        RES2: 	    Boolean(msblsb & (1 << 8)),
                        INTERVAL: 	intervall === 0 ? "THERMOMETER" :
    				                intervall === 1 ? "HISTORY" :
    				                intervall === 2 ? "1DAY" :
    				                intervall === 3 ? "2DAY" :
					                intervall === 4 ? "4DAY" :
    				                intervall === 5 ? "OPTION1" :
					                intervall === 6 ? "OPTION2" :
            		                intervall === 7 ? "OPTION3" : "undef"
            }
  	
}  	

function parseStatusBits(bytes,start_index) {
    var msblsb = (bytes[start_index]<<8);
    msblsb|= bytes[start_index+1];
    var intervall = (msblsb & 7);
    return {
      	STATUSBYTE0: bytes[start_index],
		STATUSBYTE1: bytes[start_index+1],
		ERROR_RFTRAFFIC: Boolean(msblsb & (1 << 15)),
        OPT_2F:       Boolean(msblsb & (1 << 14)),
      	ERROR_RESET: 	Boolean(msblsb & (1 << 13)),
		ERROR_RF: 		Boolean(msblsb & (1 << 12)),
        ERROR_CS: 		Boolean(msblsb & (1 << 11)),
        ERROR_BATTLOW: 	Boolean(msblsb & (1 << 10)),
      	ERROR_SABOT: 	Boolean(msblsb & (1 << 9)),
      	ERROR_MESS: 	Boolean(msblsb & (1 << 8)),
		OPT_ANZ: 	    Boolean(msblsb & (1 << 7)) ? "VERB" : "ZS",
      	OPT_RADIO: 	    Boolean(msblsb & (1 << 6)) ? "ON" : "OFF",
        OPT_LINK: 		Boolean(msblsb & (1 << 5)) ? "ON" : "OFF",
        OPT_ADR: 		Boolean(msblsb & (1 << 4)) ? "ON" : "OFF",
        INSTALL: 		Boolean(msblsb & (1 << 3)) ? "2min" : "no",
        INTERVAL: 	    intervall === 0 ? "THERMOMETER" :
    				    intervall === 1 ? "HISTORY" :
    				    intervall === 2 ? "1DAY" :
    				    intervall === 3 ? "2DAY" :
					    intervall === 4 ? "4DAY" :
    				    intervall === 5 ? "OPTION1" :
					    intervall === 6 ? "OPTION2" :
            		    intervall === 7 ? "OPTION3" : "undef"
    }
}

function parse4BytesMsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++] << 24;
    value |= bytes[index++] << 16;
    value |= bytes[index++] << 8;
    value |= bytes[index++];
    return value;
} 

function parse4BytesLsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++];
    value |= bytes[index++] << 8;
    value |= bytes[index++] << 16;
    value |= bytes[index++] << 24;
    return value;
}

function parse2BytesMsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++] << 8;
    value |= bytes[index++];
    return value;
}

function parse4BytesMSBasArray(buffer,startindex,Anzahl) {
    var result = [];
    
    for (var i = 0; i < Anzahl; i ++) {
        var value = 0;
        value |= buffer[startindex] << 24;
        value |= buffer[startindex + 1] << 16;
        value |= buffer[startindex + 2] << 8;
      	value |= buffer[startindex + 3];
		startindex = startindex + 4;
        result.push(value);
    }
    
    return result;
}

function parse2BytesMSBasArray(buffer,startindex,Anzahl) {
    var result = [];
    
    for (var i = 0; i < Anzahl; i ++) {
        var value = 0;
        value |= buffer[startindex + 0] << 8;
      	value |= buffer[startindex + 1];
		startindex = startindex + 2;
        result.push(value);
    }
    
    return result;
}

function bytesToBCD_4(bytes) {
    var bcdString = '';
    for (var i = 0; i < bytes.length; i++) {
        var byte = bytes[i];
        var upperNibble = byte >> 4;
        var lowerNibble = byte & 0x0F;
        bcdString += upperNibble.toString(16) + lowerNibble.toString(16);
    }
    return bcdString.toUpperCase().padStart(8, '0');
}

function bytesToBCD_1(bytes) {
    var bcdString = '';
    var byte = bytes;
    var upperNibble = byte >> 4;
    var lowerNibble = byte & 0x0F;
    bcdString += upperNibble.toString(16) + lowerNibble.toString(16);
    return bcdString.toUpperCase();
}

function parse4BytesToTime(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++] << 8;
    value |= bytes[index++];
    value = value <<8;
    value |= bytes[index++];
    value = value <<8;
    value |= bytes[index++];
    return {
        Sec: (value & 63),
        Min: (value >> 6) & 63,
        Std: (value >> 12) & 31,
        Tag: (value >>17) & 31,
        Mon: (value >>22) & 15,
        Jahr: ((value >>26) & 63)+2000
    }
}
  // Encode downlink function.
  //
  // Input is an object with the following fields:
  // - data = Object representing the payload that must be encoded.
  // - variables = Object containing the configured device variables.
  //
  // Output must be an object with the following fields:
  // - bytes = Byte array containing the downlink payload.
  function encodeDownlink(input) {
    return {
      bytes: [225, 230, 255, 0]
    };
  }
 