// 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}
//
// Yunhorn SmarToilets Sensor R20241030A01
//

function Decode(fPort, data, variables) {
    var data = {};
    data.length = bytes.length;
    switch (fPort) {
        // RESPOND PORT --- bottom of swith fport 
        // case 1:

        // break;

        // CMD DOWN LINK PORT
        case 2:

            break;

        // STS-R5 (LEGACY, ULTRASONIC WASTE-BIN)
        case 3:
            break;

        // STS-O1/O2/O3/O5  REEDSWITCH, OCCUPANCY LED REPORT
        case 4:
            break;
        // HEART-BEAT FOR STS-O1/O2/O3/O5  REEDSWITCH, OCCUPANCY LED REPORT
        case 5:
            break;

        // STS-M1 water leakage sensor
        case 6:
            break;

        // R4 soap/sanitizer sensor
        case 7:
            data.led_state = bytes[0] == 0x0 ? "Off" : "On";
            data.mtm_code_1 = bytes[1];
            data.mtm_code_2 = bytes[2];
            data.hw_code = bytes[3];
            data.battery_level = bytes[4] + "%";
            data.size_value = bytes[5];
            data.measure_tech = (bytes[6] === 0) ? "Capacitive" : "Other";
            data.liquid_level_event = (bytes[7] === 0x1) ? "Liquid Detected" : "No Liquid";

            return { "Yunhorn_SmarToilets_data": data };

            break;

        // STS-R3 soap yes no
        case 8:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-M3 water tank or cistern level sensor Ultrasonic
        case 9:

            break;

        // STS_O2_O6 V3 version 2023,pixel-network version
        case 10:
            switch (bytes[0]) {
                case 0x00:
                    data.LEDcolor = "Dark";
                    break;
                case 0x01:
                    data.LEDcolor = "Green";
                    data.cubicleOccupyStatus = "Vacant";
                    break;
                case 0x02:
                    data.LEDcolor = "Red";
                    data.cubicleOccupyStatus = "Occupied";
                    break;
                case 0x03:
                    data.LEDcolor = "Blue";
                    data.cubicleOccupyStatus = "Maintenance";
                    break;
                case 0x04:
                    data.LEDcolor = "Yellow";
                    data.cubicleOccupyStatus = "TBD";
                    break;
                case 0x05:
                    data.LEDcolor = "Pink";
                    data.cubicleOccupyStatus = "TBD";
                    break;
                case 0x06:
                    data.LEDcolor = "Cyan";
                    data.cubicleOccupyStatus = "TBD";
                    break;
                case 0x07:
                    data.LEDcolor = "White";
                    data.cubicleOccupyStatus = "TBD";
                    break;
                case 0x08:
                    data.LEDcolor = "Red_Blue";
                    data.cubicleOccupyStatus = "EMERGENCY";
                    break;
                case 0x23:
                    data.LEDcolor = "Red_Blue";
                    data.cubicleOccupyStatus = "EMERGENCY";
                    break;
                case 0x20:
                    data.LEDcolor = "Red_Flash";
                    data.cubicleOccupyStatus = "EMERGENCY";
                    break;
                default:
                    data.LEDcolor = "TBD_COLOR";
                    data.cubicleOccupyStatus = "TBD_status";
                    break;
            }

            switch (bytes[1]) {
                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;
            }

            // select only one below
            // For NC (Normal Closed states)
            data.Sensor1_Door_Contact_Open = bytes[2] === 0 ? "Door Closed" : "Door Open";

            // For NC (Normal Closed states)
            // data.Sensor1_Door_Contact_Open = bytes[3]===1?"Door Closed":"Door Open"; 
            if (bytes[1] == 0x02) //Hall_element_mode
            {
                data.Sensor2_SOS_Pushed = bytes[3] === 0 ? "PushDown" : "RelaseUP";
            } else if (bytes[1] > 0x02) {
                data.Sensor2_Motion_Detected = bytes[3] === 0 ? "No Motion" : "Motion Detected";
            }

            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS R5 waste bin 
        case 11:
            data.LED_State = (bytes[0] === 0) ? "Off" : "On";
            data.mtm_code_1 = bytes[1];
            data.mtm_code_2 = bytes[2];
            data.hw_code = bytes[3];
            data.battery_level = bytes[4] + "%";
            // data.battery_level_mV = (bytes[4])*100 + " mV";
            data.size_value = bytes[5];
            data.distance_1_mm = bytes[6] << 8 | bytes[7];
            // data.distance_2_mm = bytes[8]<<8|bytes[9];
            // data.distance_3_mm = bytes[10]<<8|bytes[11];
            data.distance_unit = "mm";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-M4 water flow sensor
        case 12:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-M1A  Water mark sensor thermal matrix
        case 13:
            break;

        // STS-M1A  Water mark sensor thermal matrix, heart-beat
        case 14:
            break;


        // STS-T6 O6T ToF Presence Detection
        case 14:
            data.LED_State = bytes[0] === 0 ? "Off" : "On";
            data.MTM_Code_1 = bytes[1];
            data.MTM_Code_2 = bytes[2];
            data.HW_Code = bytes[3];
            data.Battery_Level = bytes[4] + " %";
            data.Payload_Size = bytes[5];
            data.Presence_State = bytes[6] == 1 ? "Occupied" : "Vacant";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-T6 heart beat 
        case 15:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        case 16:
            break;

        case 18:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };

            break;

        // Heart-beat for STS-O6 occupancy sensor
        case 17:
        // STS-O7 Fall detection sensor
        case 19:
        case 21:
            {
                data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
                switch (bytes[1]) {
                    case 0x00:
                        data.LEDcolor = "Dark";
                        break;
                    case 0x01:
                        data.LEDcolor = "Green";
                        data.cubicleOccupyStatus = "Vacant";
                        break;
                    case 0x02:
                        data.LEDcolor = "Red";
                        data.cubicleOccupyStatus = "Occupied";
                        break;
                    case 0x03:
                        data.LEDcolor = "Blue";
                        data.cubicleOccupyStatus = "Maintenance";
                        break;
                    case 0x04:
                        data.LEDcolor = "Yellow";
                        data.cubicleOccupyStatus = "TBD";
                        break;
                    case 0x05:
                        data.LEDcolor = "Pink";
                        data.cubicleOccupyStatus = "TBD";
                        break;
                    case 0x06:
                        data.LEDcolor = "Cyan";
                        data.cubicleOccupyStatus = "TBD";
                        break;
                    case 0x07:
                        data.LEDcolor = "White";
                        data.cubicleOccupyStatus = "TBD";
                        break;
                    case 0x08:
                        data.LEDcolor = "Red_Blue";
                        data.cubicleOccupyStatus = "EMERGENCY";
                        break;
                    case 0x23:
                        data.LEDcolor = "Red_Blue";
                        data.cubicleOccupyStatus = "EMERGENCY";
                        break;
                    case 0x20:
                        data.LEDcolor = "Red_Flash";
                        data.cubicleOccupyStatus = "EMERGENCY";
                        break;
                    default:
                        data.LEDcolor = "TBD_COLOR";
                        data.cubicleOccupyStatus = "TBD_status";
                        break;
                }

                switch (bytes[2]) {
                    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;
                }
                // select only one below
                // For NC(Normal Closed states
                data.Sensor1_Door_Contact_Open = bytes[3] === 0 ? "Door Closed" : "Door Open";

                // For NC(Normal Closed states
                //data.Sensor1_Door_Contact_Open = bytes[3]===1?"Door Closed":"Door Open"; 
                data.Sensor2_Emergency_Button = bytes[4] === 0 ? "Alarm Push Down" : "No Alarm, Released";
                data.Sensor3_Motion_Detected = bytes[5] === 0 ? "No Motion" : "Motion Detected";

                data.length = bytes.length;
                if (data.length === 9) {
                    data.Over_stay_state = (bytes[6] === 0) ? "False" : "True";
                    data.Over_Stay_duration_in_Seconds = (bytes[7] << 8 | bytes[8]);

                    return { "Yunhorn_SmarToilets_data": data };
                }
                else if (data.length > 9) {
                    data.Sensor4_ALARM_MUTE = (bytes[6] === 0) ? "Down Mute" : "No Mute";
                    data.Sensor5_ALARM_RESET = (bytes[7] === 0) ? "Down RESET" : "NO Reset";
                    data.Distance_in_mm = (bytes[8] << 8 | bytes[9]);
                    data.MotionLevel = (bytes[10] << 8 | bytes[11]);

                    data.Unconcious_State = (bytes[12] == 0) ? "False" : "True";

                    switch (bytes[13]) {
                        case 0x0:
                            data.Fall_Down_Detected_State = "Presence_Normal";
                            break;
                        case 0x01:
                            data.Fall_Down_Detected_State = "Presence_Fall_Down";
                            break;
                        case 0x02:
                            data.Fall_Down_Detected_State = "Presence_Rising_Up";
                            break;
                        case 0x03:
                            data.Fall_Down_Detected_State = "Presence_LayDown";
                            break;
                        case 0x04:
                            data.Fall_Down_Detected_State = "Presence_Unconcious";
                            break;
                        case 0x05:
                            data.Fall_Down_Detected_State = "Presence_Stay_Still";
                            break;
                        default:
                            data.Fall_Down_Detected_State = "Presence_Normal";
                            break;
                    }
                    data.OverStay_Detected_State = (bytes[14] == 0x0) ? "False" : "True";
                    data.OverStay_Duration_in_Seconds = (bytes[15] << 8 | bytes[16]);
                    data.No_Movement_Duration_in_Seconds = (bytes[17] << 8 | bytes[18]);
                    data.Unconcious_Duration_in_Seconds = (bytes[17] << 8 | bytes[18]);
                    data.Fall_Down_Speed_in_m_per_s = (bytes[19]);
                    data.Fall_Down_Gravity_in_g = (bytes[20]);
                    data.SOS_PushDown_Stamp = (bytes[21] << 24 | bytes[22] << 16 | bytes[23] << 8 | bytes[24]);
                    if (data.SOS_PushDown_Stamp != 0) {
                        var sos_start = new Date(1000 * data.SOS_PushDown_Stamp);
                        data.SOS_PushDown_Time = "[" + sos_start.getDate() + "." + (sos_start.getMonth() + 1) + "." + (sos_start.getFullYear()) + "] " + sos_start.getHours() + ":" + sos_start.getMinutes() + ":" + sos_start.getSeconds();
                    } else data.SOS_PushDown_Time = "N/A";
                    data.SOS_ReleaseUP_Stamp = (bytes[25] << 24 | bytes[26] << 16 | bytes[27] << 8 | bytes[28]);
                    if (data.SOS_ReleaseUP_Stamp != 0) {
                        var sos_stop = new Date(1000 * data.SOS_ReleaseUP_Stamp);
                        data.SOS_ReleaseUP_Time = "[" + sos_stop.getDate() + "." + (sos_stop.getMonth() + 1) + "." + (sos_stop.getFullYear()) + "] " + sos_stop.getHours() + ":" + sos_stop.getMinutes() + ":" + sos_stop.getSeconds();
                    } else data.SOS_ReleaseUP_Time = "N/A";

                    data.Fall_Down_Stamp = (bytes[29] << 24 | bytes[30] << 16 | bytes[31] << 8 | bytes[32]);
                    if (data.Fall_Down_Stamp != 0) {
                        var fall_start = new Date(1000 * data.Fall_Down_Stamp);
                        data.Fall_Down_Time = "[" + fall_start.getDate() + "." + (fall_start.getMonth() + 1) + "." + (fall_start.getFullYear()) + "] " + fall_start.getHours() + ":" + fall_start.getMinutes() + ":" + fall_start.getSeconds();
                    } else data.Fall_RiseUp_Stamp = "N/A";
                    data.Fall_RiseUp_Stamp = (bytes[33] << 24 | bytes[34] << 16 | bytes[35] << 8 | bytes[36]);
                    if (data.Fall_RiseUp_Stamp != 0) {
                        var fall_stop = new Date(1000 * data.Fall_RiseUp_Stamp);
                        data.Fall_RiseUp_Time = "[" + fall_stop.getDate() + "." + (fall_stop.getMonth() + 1) + "." + (fall_stop.getFullYear()) + "] " + fall_stop.getHours() + ":" + fall_stop.getMinutes() + ":" + fall_stop.getSeconds();
                    } else data.Fall_RiseUp_Time = "N/A";
                }
                return { "Yunhorn_SmarToilets_data": data };
            }




            break;


            break;

        // STS-O6 occupancy sensor heart-beat
        case 18:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-O7 Fall detection sensor heart-beat
        case 20:
            break;

        // STS-M7 Vibration sensor
        case 20:
            break;




        // STS R3 Soap Yes_No sensor
        case 51:
            data.LED_State = (bytes[0] === 0) ? "Off" : "On";
            data.mtm_code_1 = bytes[1];
            data.mtm_code_2 = bytes[2];
            data.hw_code = bytes[3];
            data.battery_level = bytes[4] + "%";
            // data.battery_level_mV = (bytes[4])*100 + " mV";
            data.size_value = bytes[5];
            data.Liquid_detected = bytes[6];
            data.Detect_method = (bytes[7] === 0) ? "Capacitive" : "ToF";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS R2 Tissue paper sensor
        case 52:
            data.LED_State = (bytes[0] === 0) ? "Off" : "On";
            data.mtm_code_1 = bytes[1];
            data.mtm_code_2 = bytes[2];
            data.hw_code = bytes[3];
            data.battery_level = bytes[4] + "%";
            // data.battery_level_mV = (bytes[4])*100 + " mV";
            data.size_value = bytes[5];
            data.distance_1_mm = bytes[6] << 8 | bytes[7];
            // data.distance_2_mm = bytes[8]<<8|bytes[9];
            // data.distance_3_mm = bytes[10]<<8|bytes[11];
            data.distance_unit = "mm";
            return { "Yunhorn_SmarToilets_data": data };
            break;


        // STS-M8 QUEUE BAR SENSOR
        case 55:

        // STS-O4 URINAL OCCUPANCY SENSOR
        case 56:

            break;

        // R1D dual roll toilet paper sensor
        case 57:
            data.LED_State = (bytes[0] === 0) ? "Off" : "On";
            data.mtm_code_1 = bytes[1];
            data.mtm_code_2 = bytes[2];
            data.hw_code = bytes[3];
            data.battery_level = bytes[4] + "%";
            //data.battery_level_mV = (bytes[4])*100 + " mV";
            data.size_value = bytes[5];
            data.distance_1_mm = bytes[6] << 8 | bytes[7];
            data.distance_2_mm = bytes[8] << 8 | bytes[9];
            // data.distance_3_mm = bytes[10]<<8|bytes[11];
            data.distance_unit = "mm";
            return { "Yunhorn_SmarToilets_data": data };
            break;

        // STS-R1 Single roll toilet paper sensor
        case 58:
            data.LED_State = (bytes[0] === 0) ? "Off" : "On";
            //data.mtm_code_1 = bytes[1];
            //data.mtm_code_2 = bytes[2];  
            //data.hw_code = bytes[3];
            //data.battery_level = bytes[1]+"%";
            data.battery_level_mV = (bytes[1]) * 100 + " mV";
            //data.size_value = bytes[5]; 
            //data.distance_1_mm = bytes[6]<<8|bytes[7];
            //data.distance_2_mm = bytes[8]<<8|bytes[9];
            // data.distance_3_mm = bytes[10]<<8|bytes[11];
            //data.distance_unit = "mm";
            return { "Yunhorn_SmarToilets_data": data };
            break;


        // STS-M2 TOILET CLOGGING SENSOR
        case 59:

            break;


        // STS-E4 PTAQ, ODOR LEVEL Sensor 4-IN-1
        case 70:

            break;

        // STS-E6 PTAQ, ODOR LEVEL Sensor 6-IN-1
        case 72:

            break;

        // STS-E5 PTAQ, ODOR LEVEL Sensor  5-IN-1
        case 74:

            break;


        // STS-E2 PTAQ, ODOR LEVEL Sensor
        case 101:

            break;

        // STS-E1 Smoking Sensor
        case 102:

            break;

        // STS-P1 QUEUEING SENSOR      
        case 104:

            break;

        // STS-M6 POWER RELAY SENSOR      
        case 105:
            break;

        // STS-P2 IN-OUT PEOPLE COUNTING SENSOR      
        case 106:
            {
                //  STS-P2 bi-directional people counting        
                data.LED_State = bytes[0] === 0 ? "Off" : "On";
                data.MTM_Code_1 = bytes[1];
                data.MTM_Code_2 = bytes[2];
                data.HW_Code = bytes[3];
                data.Battery_Level = bytes[4] + " %";
                data.Payload_Size = bytes[5];
                data.Walk_In_People_Count = bytes[6] << 8 | bytes[7];
                data.Walk_Out_People_Count = bytes[8] << 8 | bytes[8];
                data.Walk_Around_People_Count = bytes[10] << 8 | bytes[11];
                data.Count_Period = bytes[12];
                data.Count_Period_Unit = String.fromCharCode(bytes[13]);
                data.Sum_Day_Walk_In_People_Count = bytes[14] << 8 | bytes[15];
                data.Sum_Day_Walk_Out_People_Count = bytes[16] << 8 | bytes[17];
                data.Sum_Day_Walk_Around_People_Count = bytes[18] << 8 | bytes[19];
                data.Count_Valid = (bytes[20] == 0x0) ? "Error" : "OK";

                return { "Yunhorn_SmarToilets_data": data };
            }
            break;

        // HEART-BEAT FOR STS-P2 IN-OUT PEOPLE COUNTING SENSOR      
        case 107:
            //data.led_state=(bytes[0] & 0x7f) === 0 ? "Off" : "On";
            data.BoardLED = ((bytes[0] & 0x7F) === 0x01) ? "ON" : "OFF";
            //data.battery_level = bytes[1] + " %";
            data.battery_level = bytes[1] * 100 + "mV";
            return { "Yunhorn_SmarToilets_data": data };
            break;


        // respond port
        case 1:
            switch (bytes[0]) {
                // NVM config  'C'
                case 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];

                    break;

                // D DISTANCE , INSTALLATION HEIGHT REPORT
                case 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";

                    break;

                // LoRa Class   'L'
                case 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]);
                    break;


                // CONTROL COMMAND 'P'
                case 0x50:
                    // P-CMD work mode change
                    switch (data.length) {
                        case 4:
                            if ((data.length === 4) && (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;
                                }
                            }

                            break;
                        case 7:
                            if (bytes[3] === 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;
                        case 8:
                            if (bytes[3] === 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");
                            } else if (bytes[3] === 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;
                        case 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)) + " %";
                            break;
                        case 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);

                            break;

                        default:
                            break;

                    }


                    break;

                // CONTROL COMMAND 'R'
                case 0x52:
                    if (bytes[1] === 0x46) {
                        data.RFAC_Process_Request = "OK";
                        data.RFAC_Req = String.fromCharCode(bytes);
                    }
                    break;


                // Self-Test function 'S'
                case 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";
                    }

                    break;

                case 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;

                    break;


                // Firmware version 'V'            
                case 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;
                    }
                    break;

                // Respond 'Y'            
                case 0x59:

                    switch (bytes[1]) {
                        // Uplink Duration interval
                        case 0x44:
                            data.Uplink_interval = (bytes[2] - 0x30) * 10 + (bytes[3] - 0x30);
                            data.Uplink_interval_unit = String.fromCharCode(bytes[4]);
                            break;

                        // Sampling interval or Heart-beat interval, 'YSaab', 'YLaab'
                        case 0x4C:
                        case 0x53:
                            data.Heart_Beat_interval = (bytes[2] - 0x30) * 10 + (bytes[3] - 0x30);
                            data.Heart_Beat_interval_unit = String.fromCharCode(bytes[4]);
                            // if sts_mtm1 == O6/O7 ....TODO XXXX
                            // data.Wakeup_sampling_interval = (bytes[2]-0x30)*10 + (bytes[3]-0x30);
                            // data.Unit = String.fromCharCode(bytes[4]);
                            break;

                    }

                    if (bytes[9] == 0x38) {
                        data.Yunhorn_PRD = "True";
                        data.PRD_String = String.fromCharCode(bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16]);
                    }

                    break;


                default:
                    break;

            }
            switch (data.length) {
                // RFAC process
                case 4:
                    if ((bytes[0] == 0x52) && (bytes[1] == 0x46)) {
                        data.RFAC_Process_Request = "OK";
                        data.RFAC_Req = String.fromCharCode(bytes);
                    } else {
                        if ((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]);
                        }
                    }
                    break;

                // YUNHORN PRD MARK
                case 10:
                    if ((bytes[0] == 0x59) && (bytes[9] == 0x38))
                        data.Yunhorn_PRD = "True";
                    data.PRD_String = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6]);
                    break;

                // YUNHORN NVM CONFIG LENGTH
                case 44:
                    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];
                    }

                    break;
                default:
                    break;

            }

    }
    return { "Yunhorn_SmarToilets_data": data };

}

//
// Yunhorn SmarToilets Sensor Decoder
//