// Decode decodes an array of bytes into an object.
//  - fPort contains the LoRaWAN fPort number
//  - bytes is an array of bytes, e.g. [225, 230, 255, 0]
//  - variables contains the device variables e.g. {"calibration": "3.5"} (both the key / value are of type string)
// The function must return an object, e.g. {"temperature": 22.5}
function Decode(fPort, bytes, variables) {
  // O5 Door contact sensor
  if (fPort === 4) {
  	return [
      {
        led_state: bytes[0]===0 ? "Off":"On",
        mtm_code_1: bytes[1],  
        mtm_code_2: bytes[2],  
        hw_code: bytes[3],
        battery_level: bytes[4]+"%",
        size_value: bytes[5],      
        door_state: bytes[6] === 1 ? "Closed": "Open",
      }
    ];
  }
  // heart-beat of O5
  else if (fPort === 5) {
    return [
      {
       led_state: (bytes[0] & 0x7f) === 0 ? "Off" : "On",
       battery_level: bytes[1] + " %",
       }
    ];
  }
  // R4 soap/sanitizer sensor
  else if (fPort === 7) {
    return [
      {
        led_state: bytes[0]===0 ? "Off":"On",
        mtm_code_1: bytes[1],  
        mtm_code_2: bytes[2],  
        hw_code: bytes[3],
        battery_level: bytes[4]+"%",
        size_value: bytes[5],      
        measure_tech:bytes[6] === 0? "Capacitive":"Other",
        liquid_level_event: bytes[7] === 0? "Liquid Detected":"No Liquid",
      }
    ];
  }
  // R4 soap/sanitizer heart-beat
  else if (fPort === 8) {
    return [
      {
       led_state: (bytes[0] & 0x7f) === 0 ? "Off" : "On",
       //battery_level: bytes[1] + " %",
        battery_level_mV: bytes[1]*100 + " mV",
      }
    ];
  }
  // R1D dual roll toilet paper sensor
  else if (fPort === 57) {
    return [
      { 
        led_state:bytes[0]===0 ? "Off":"On",
        mtm_code_1:bytes[1],  
        mtm_code_2:bytes[2],  
        hw_code:bytes[3],
        battery_level:bytes[4]+"%",
        size_value:bytes[5], 
        distance_1_mm:bytes[6]*256+bytes[7],
        distance_2_mm:bytes[8]*256+bytes[9],
        distance_unit: "mm",
     }
    ];
  }
    // R1D, Heart-beat 
    else if (fPort === 58) {
      return [
        {
         led_state: (bytes[0] & 0x7f) === 0 ? "Off" : "On",
         //battery_level: bytes[1] + " %",
          battery_level_mV: bytes[1]*100 + " mV",
        }
      ];
    }
  // R5 waste bin sensor
  else if (fPort === 11) {
    return [
      {
        led_state:bytes[0]===0 ? "Off":"On",
        mtm_code_1:bytes[1],  
        mtm_code_2:bytes[2],  
        hw_code:bytes[3],
        battery_level:bytes[4]+"%",
        size_value:bytes[5],      
        distance_mm: bytes[6]*256 + bytes[7],
        distance_unit: "mm",
     }
   ];
  }
 // R5, Heart-beat 
    else if (fPort === 12) {
      return [
        {
         led_state: (bytes[0] & 0x7f) === 0 ? "Off" : "On",
         //battery_level_percent: bytes[1] + " %",
          battery_level_mV: bytes[1]*100 + "mV",
        }
      ];
    }

  // UPLINK, RFAC
	else if (fPort === 1 ) 
    {    
	    var data={};
		data.length = bytes.length;
        if ((data.length ===10) && (bytes[0] == 0x59)&&(bytes[9]==0x38))
        {
          data.Yunhorn_PRD ="True";
          data.PRD_String = String.fromCharCode(bytes);
        }
        else if ((data.length ===4) && (bytes[0] == 0x52)&&(bytes[1]==0x46)) //RFAC
        {
          data.Request_Performed = "OK";
		  data.RFAC = "OK";
		  data.AC0 = bytes[0];
		  data.AC1 = bytes[1];        
        }
        else if ((data.length ===4) && (bytes[0]===0x50)&&(bytes[1]===0x31)&&(bytes[2]===0x31))
        {   data.Work_Mode_Switch ="OK";
            switch (bytes[3]-0x30) 
            {
                case 0x0:
                    data.workmode = "Network_mode";
                break;
                case 0x01:
                    data.workmode = "Wired_Mode";
                break;
                case 0x02:
                    data.workmode = "Hall_element_mode";
                break;
                case 0x03:
                    data.workmode = "MotionDetect_mode";
                break;
                case 0x04:
                    data.workmode = "Dual_mode";
                break;
                  case 0x05:
                    data.workmode = "Uni_Mode";
                    break;
                default:
                    data.workmode = "Unknown Mode";
                break;
            }
        }
                
        else if ((bytes[0]===0x59) && (bytes[1] === 0x44)) //Duration interval
        {
            data.Uplink_interval=(bytes[2]-0x30)*10+(bytes[3]-0x30);
            data.Uplink_interval_unit=String.fromCharCode(bytes[4]);          
            //data.Heart_beat_Duration = (bytes[2]-0x30)*10 + (bytes[3]-0x30);
            //data.Unit = String.fromCharCode(bytes[4]);
         }
      
        else if ((bytes[0]===0x59) && (bytes[1] === 0x53))	//Sampling interval or Heart-beat interval
        {
          data.Heart_Beat_interval=(bytes[2]-0x30)*10+(bytes[3]-0x30);
          data.Heart_Beat_interval_unit=String.fromCharCode(bytes[4]);
          //data.Wakeup_sampling_interval = (bytes[2]-0x30)*10 + (bytes[3]-0x30);
          //data.Unit = String.fromCharCode(bytes[4]);
        }
      
        else if (bytes[0]===0x43) {  // report current nvm config
         data.mtm_code1=bytes[1];
         data.mtm_code2=bytes[2];
         data.sts_verion=bytes[3];
         data.sts_hw_ver=bytes[4];
         data.sts_uplink_interval = bytes[5];
         data.sts_uplink_interval_unit = String.fromCharCode(bytes[6]);
         data.sts_sampling_interval = bytes[7];
         data.sts_sampling_interval_unit = String.fromCharCode(bytes[8]);
         data.sts_work_mode=bytes[9];
          data.sts_service_mask=bytes[10];
          data.sts_reserve01=bytes[11];
          data.sts_payload_length=bytes[12];
         data.rss_start_distance=bytes[13]*0.1+" meter";
         data.rss_range_length = bytes[14]*0.1+" meter";
         data.rss_threshold = bytes[15]*0.1;
         data.rss_receiving_gain=bytes[16]+" %";
         data.rss_profile=bytes[17];
         data.rss_rate_tracking=bytes[18];
         data.rss_rate_presence=bytes[19];
         data.rss_HWAAS = bytes[20];
         data.rss_nbr_removed_pc=bytes[21];
         data.rss_inter_frame_deviation_time_const=bytes[22]*0.1;
         data.rss_inter_frame_fast_cutoff=bytes[23];
         data.rss_inter_frame_slow_cutoff=bytes[24];
         data.rss_intra_frame_time_const=bytes[25];
         data.rss_intra_frame_weight=bytes[26]*0.1;
         data.rss_output_time_const=bytes[27]*0.1;
         data.rss_downsampling_factor=bytes[28];
         data.rss_power_saving_mode_active=bytes[29];
         data.rss_reserve02=bytes[30];
         data.rss_reserve03=bytes[31];
         data.rss_reserve04=bytes[32];
         data.reserve2=bytes[33];
         data.reserve3=bytes[34];
         data.sensor_install_height=bytes[35]*10+" cm";
          
         data.alarm_parameter05=bytes[36];          
         data.alarm_mute_expire_timer=bytes[37];
         data.alarm_lamp_bar_flashing_color=bytes[38]; 
         data.occupancy_overtime_threshold=bytes[39];          
         
         data.motionless_duration_threshold=bytes[40];
         data.unconcious_threshold = bytes[41];
          
         data.fall_detection_acc_threshold=bytes[42];
         data.fall_detection_depth_threshold=bytes[43]; 
         data.fall_down_confirm_threshold=bytes[44];                  
       }
       else if (bytes[0]===0x53) 
       {  // SELF TEST FUNCTION
         data.mtm_code1=bytes[1];
         data.mtm_code2=bytes[2];
         data.sts_verion=bytes[3];
         data.sts_hw_ver=bytes[4];
         data.battery_level=bytes[5];
         if ((bytes[6]===0x03)) {	// report sensor install height
             data.sts_sensor_install_height= (bytes[7]<<8|bytes[8]);
             data.sts_sensor_install_height_unit="mm";
             }
         else if ((bytes[6]===0x58)) 
         {
           data.sts_Test_Result="### Motion Sensor Not Detected ###";
         } else if ((bytes[6]===0x0E)) //result length, 10 rss bytes, 4 distance bytes
         {
             data.sts_Test_Result="Motion Sensor Test Result:";
             data.sts_test_result_length=bytes[6];
             data.sts_rss_sub_code1=bytes[7];
             data.sts_rss_sub_code2=bytes[8];
             data.sts_rss_sub_code3=bytes[9];
             data.sts_rss_sub_code4=bytes[10];
             data.sts_rss_sub_code5=bytes[11];
             data.sts_rss_sub_code6=bytes[12];
             data.sts_rss_sub_code7=bytes[13];
             data.sts_rss_sub_code8=bytes[14];
             data.sts_rss_sub_code9=bytes[15];
             data.sts_rss_sub_code10=bytes[16];
             data.sts_sensor_install_height = String.fromCharCode(bytes[17])+String.fromCharCode(bytes[18])+String.fromCharCode(bytes[19])+String.fromCharCode(bytes[20])+" mm";
         }
      }
      else if (bytes[0]===0x44) 
      {  // MEASURE INSTALLATION HEIGHT
		 data.mtm_code1=bytes[1];
         data.mtm_code2=bytes[2];
         data.sts_verion=bytes[3];
         data.sts_hw_ver=bytes[4];
         data.battery_level=bytes[5];        
         data.payload_length=bytes[6];
         data.Measure_Distance=String.fromCharCode(bytes[7])+String.fromCharCode(bytes[8])+String.fromCharCode(bytes[9])+String.fromCharCode(bytes[10]);
         data.Distance_unit="mm";
      }
      else if (bytes[0] === 0x4c) { // LoRaWAN Class A/B/C
        data.mtm_code1=bytes[1];
        data.mtm_code2=bytes[2];
        data.sts_verion=bytes[3];
        data.LoRaWAN_Class=String.fromCharCode(bytes[4]);        
      }
      else if (bytes[0]===0x56) 
      {	 // FIRMWARE VERSION
        
        data.mtm_code1=bytes[1];
        data.mtm_code2=bytes[2];
        data.sts_verion=bytes[3];
        data.sts_hw_ver=bytes[4];
        data.sts_major=bytes[5];
        data.sts_minor=bytes[6];
        data.subminor=bytes[7];
        
        if (data.length ===15) {
        data.L_year=(bytes[8]<<8|bytes[9]);
        data.L_mon=bytes[10];  
        data.L_day=bytes[11];
        data.L_hour=bytes[12];
        data.L_min=bytes[13];        
        data.L_sec=bytes[14];
        data.LocalTime_UTC="UTC:  "+data.L_year+"/"+data.L_mon+"/"+data.L_day+" "+data.L_hour+":"+data.L_min+":"+data.L_sec;         
        data.LocalTime_EST8="GMT+8: "+data.L_year+"/"+data.L_mon+"/"+data.L_day+" "+(data.L_hour+8)+":"+data.L_min+":"+data.L_sec;         
      }
      }
      else if (bytes[0] === 0x54)
      {  //T: date & time
        data.L_year=(bytes[1]<<8|bytes[2]);
        data.L_mon=bytes[3];  
        data.L_day=bytes[4];
        data.L_hour=bytes[5];
        data.L_min=bytes[6];        
        data.L_sec=bytes[7];
        data.LocalTime_UTC="UTC:  "+data.L_year+"/"+data.L_mon+"/"+data.L_day+" "+data.L_hour+":"+data.L_min+":"+data.L_sec;         
  
      }
      else if (bytes[0]===0x50) 
      { // P cmd report
        if (data.length ==7) 
        {
        switch (bytes[3]) {
          case 0x46:   // F --- fall down & unconscious detection threshold
            data.FALL_acceleration = (bytes[4]==0x30?"Disabled":((bytes[4]-0x30)*10)+" mg/s2");
            data.FALL_depth_measure = (bytes[5]==0x30?"Disabled":((bytes[5]-0x30)*10)+" cm");
            data.FALL_confirm_threshold = (bytes[6]==0x30?"Disabled":((bytes[6]-0x30)*10)+" seconds");
            //data.FALL_reserved = (bytes[7]==0x0?"Disabled":((bytes[6]-0x30)*10)+" min");
            break;            
        } //switch
        }
        else if (data.length ==8) 
        {
        switch (bytes[3]) {
          case 0x4f:  // O -- over stay, onconscious, long stay
            data.OMU_Motionless_duration_in_min = (bytes[4]==0x30?"Disabled":((bytes[4]-0x30))+" Min");
            data.OMU_Long_Occupy_duration_in_Min= (bytes[5]==0x30?"Disabled":((bytes[5]-0x30)*10)+" Min");
            data.OMU_Unconcious_Threshold = (bytes[6]==0x30?"Disabled":((bytes[6]-0x30)*100)+"ml");
            data.OMU_Alarm_Mute_Reset_Timer = (bytes[7] ==0x30?"Disabled":((bytes[7]-0x30)*10)+" Seconds");
            break;
          case 0x46:   // F --- fall down & unconscious detection threshold
            data.FALL_acceleration = (bytes[4]==0x30?"Disabled":((bytes[4]-0x30)*10)+" mg/s2");
            data.FALL_depth_measure = (bytes[5]==0x30?"Disabled":((bytes[5]-0x30)*10)+" cm");
            data.FALL_confirm_threshold = (bytes[6]==0x30?"Disabled":((bytes[6]-0x30)*10)+" seconds");
            data.FALL_reserved = (bytes[7]==0x30?"Disabled":((bytes[6]-0x30)*10)+" min");
            break;            
        } //switch
        } else if (data.length == 11) { // P 1108201365
          data.RSS_SIMPLE_Start = ((bytes[3]-0x30)*100+(bytes[4]-0x30)*10)+" cm";
          data.RSS_SIMPLE_Length = ((bytes[5]-0x30)*100+(bytes[6]-0x030)*10)+" cm";
          data.RSS_SIMPLE_Threshold = ((bytes[7]-0x30)*1000+(bytes[8]-0x30)*100)+" ml";
          data.RSS_SIMPLE_Gain = ((bytes[9]-0x30)*10+(bytes[10]-0x30))+" %";
        } else if (data.length == 33) { // P 11
          data.RSS_FULL_Start = ((bytes[3]-0x30)*100+(bytes[4]-0x30)*10)+" cm";
          data.RSS_FULL_Length = ((bytes[5]-0x30)*100+(bytes[6]-0x30)*10)+" cm";
          data.RSS_FULL_Threshold= ((bytes[7]-0x30)*1000+(bytes[8]-0x30)*100)+" ml";
          data.RSS_FULL_Gain = ((bytes[9]-0x30)*10+(bytes[10]-0x30))+" %";
          data.RSS_FULL_Profile = (bytes[11]-0x30);
          data.RSS_FULL_Rate_Tracking = ((bytes[12]-0x30)*10+(bytes[13]-0x30));
          data.RSS_FULL_Rate_Presence = ((bytes[14]-0x30)*10+(bytes[15]-0x30));
          data.RSS_FULL_HWAAS = ((bytes[16]-0x30)*10+(bytes[17]-0x30));
          data.RSS_FULL_Num_Removed_PC = (bytes[18]-0x30);
          data.RSS_FULL_Inter_Deviation_Time_Const_in_Sec = ((bytes[19]-0x30)+(bytes[20]-0x30)*0.1);
          data.RSS_FULL_Inter_Fast_Cut_Off = ((bytes[21]-0x30)*10+(bytes[22]-0x30));
          data.RSS_FULL_Inter_Slow_Cut_Off = ((bytes[23]-0x30)*0.1+(bytes[24]-0x30)*0.001);
          data.RSS_FULL_Inter_Time_Const_in_Sec = ((bytes[25]-0x30)*10+(bytes[26]-0x30));
          data.RSS_FULL_Inter_Weight = ((bytes[27]-0x30)+(bytes[28]-0x30)*0.1);
          data.RSS_FULL_Output_Time_Const_in_Sec = ((bytes[29]-0x30)+(bytes[30]-0x30)*0.1);
          data.RSS_FULL_DownSampling_factor = (bytes[31]-0x30);
          data.RSS_FULL_Power_Saving_mode = (bytes[32]-0x30);
          
          
        }
      }
      return {"Yunhorn_SmarToilets_data": data };  
 }
}