Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. General Discussion
  3. API Structure Datatypes

API Structure Datatypes

Scheduled Pinned Locked Moved General Discussion
3 Posts 3 Posters 1.1k Views 5 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • AndyDHillA Offline
    AndyDHillA Offline
    AndyDHill
    wrote on last edited by
    #1

    In the Serial API pages we can find a table that show the correlation, between type of sensor and data structure that can be used , eg a switch will be of type S_Binary and S_ Binary has elements of V_Status & V_Watt so far all well and good. But when we come to write C code be should be disciplined in our data type and we should be typecasting or ensuring that the data we pass into V_Watt is correct type w shouldn;t be passing in a DINT when V_Watt if it is defined as type Real (float), Similarly we should only pass Bool into S_Status if it is defined as bool, but it could be defined as sint?

    This leads to my Question, is there a table showing the data types (Bool, Float, Int, Din, Sint etc) for each of these elements. ?

    Type		                  Data Type 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    V_TEMP		                     ?INT
    V_HUM		                     ?INT
    V_STATUS		             ?BOOL
    V_PERCENTAGE		             ?INT
    V_PRESSURE		             ?INT
    V_FORECAST		             ??????
    V_RAIN		
    V_RAINRATE		
    V_WIND		
    V_GUST		
    V_DIRECTION		
    V_UV		
    V_WEIGHT		
    V_DISTANCE		
    V_IMPEDANCE		
    V_ARMED		
    V_TRIPPED		
    V_WATT		
    V_KWH		
    V_SCENE_ON		
    V_SCENE_OFF		
    V_HVAC_FLOW_STATE		
    V_HVAC_SPEED		
    V_LIGHT_LEVEL		
    V_VAR1		
    V_VAR2		
    V_VAR3		
    V_VAR4		
    V_VAR5		
    V_UP		
    V_DOWN		
    V_STOP		
    V_IR_SEND		
    V_IR_RECEIVE		
    V_FLOW		
    V_VOLUME		
    V_LOCK_STATUS		
    V_LEVEL		
    V_VOLTAGE		
    V_CURRENT		
    V_RGB		
    V_RGBW		
    V_ID		
    V_UNIT_PREFIX		
    V_HVAC_SETPOINT_COOL		
    V_HVAC_SETPOINT_HEAT		
    V_HVAC_FLOW_MODE		
    V_TEXT		
    V_CUSTOM		
    V_POSITION		
    V_IR_RECORD		
    V_PH		
    V_ORP		
    V_EC		
    V_VAR		
    V_VA		
    V_POWER_FACTOR		
    
    
    

    So can anyone complete the above table ?

    K 1 Reply Last reply
    0
    • H Offline
      H Offline
      hek
      Admin
      wrote on last edited by
      #2

      This little Javascript data structure might help you (used by a thing I'm working on). Not 100% mappable to C but you should be able to read out the assumed data types.

      let parseB = function(x) {
        return x === "1" ? 1:0;
      };
      
      let serializeB = function(x) {
        if (!x)
          return "0";
        return x==="1"?"1":"0";
      };
      
      let parseS = function(x) {
        return striptags(x.toString());
      };
      
      let serializeS = function(x) {
        if (!x)
          return "";
        return x.toString();
      };
      
      let parseRGB = function(x) {
        if (x.length() != 6)
          return null;
        try {
          let r = parseInt(x.substr(0,2), 16);
          let g = parseInt(x.substr(2,2), 16);
          let b = parseInt(x.substr(4,2), 16);
          if (isNaN(r) && isNaN(g) && isNaN(b))
            return null;
          return {r: r, g: g, b: b };
        } catch (e) {
        } 
        return null;
      };
      
      let serializeRGB = function(x) {
        if (!x || !x.r || !x.g || !x.b)
          return "";
        return x.r.toString(16) + x.g.toString(16) + x.b.toString(16);
      };
      
      let parseRGBW = function(x) {
        if (x.length() != 8)
          return null;
        try {
          let r = parseInt(x.substr(0,2), 16);
          let g = parseInt(x.substr(2,2), 16);
          let b = parseInt(x.substr(4,2), 16);
          let w = parseInt(x.substr(6,2), 16);
          if (isNaN(r) && isNaN(g) && isNaN(b) && isNaN(w))
            return null;
          return {r: r, g: g, b: b, w: w };
        } catch (e) {
          return null;
        } 
      };
      
      let serializeRGBW = function(x) {
        if (!x || !x.r || !x.g || !x.b || !x.w)
          return "";
        return x.r.toString(16) + x.g.toString(16) + x.b.toString(16) + x.w.toString(16);
      };
      
      
      let parsePos = function(x) {
        return x.split(";");
      };
      
      
      let serializePos = function(x) {
        if (!x || !Array.isArray(x))
          return "";
        return x.join(";");
      };
      
      let parseF = function(x) {
        try {
          let f = Number.parseFloat(x); 
          return isNaN(f) ? null : f;
        } catch (e) {
           return null;
        }
      };
      
      let serializeF = function(x) {
        if (x===undefined || x==="")
          return "";
        return x.toString();
      };
      
      let parseI = function(x) {
        try {
          let i = Number.parseInt(x, 10);
          return isNaN(i) ? null : i;
        } catch (e) {
           return null;
        }
      };
      
      let serializeI = function(x) {
        if (!x)
          return "";
        return x.toString();
      };
      
      
      let parseP = function(x) {
        try {
            let i = Number.parseInt(x, 10);
            return isNaN(i) ? null : Math.min(Math.max(0,i),100);
        } catch (e) {
          return null;
        }
      };
      
      let serializeP = function(x) {
        if (!x)
          return "0";
        return x.toString();
      };
      
      
      let allowedForecast = ["stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown"];
      let parseForecast = function(x) {
        if(allowedForecast.indexOf(x)>=0)
          return x;
        return "unknown";    
      };
      
      let serializeForecast = function(x) {
        if(allowedForecast.indexOf(x)>=0)
          return x;
        return "unknown";
      };
      
      let allowedFlowState = ["Off", "HeatOn", "CoolOn", "AutoChangeOver"];
      let parseFlowState = function(x) {
        if(allowedFlowState.indexOf(x)>=0)
          return x;
        return null;    
      };
      
      let serializeFlowState = function(x) {
        if(allowedFlowState.indexOf(x)>=0)
          return x;
        return "";
      };
      
      let allowedHvacSpeed = ["Min", "Normal", "Max", "Auto"];
      let parseHvacSpeed = function(x) {
        if(allowedHvacSpeed.indexOf(x)>=0)
          return x;
        return null;    
      };
      
      let serializeHvacSpeed = function(x) {
        if(allowedHvacSpeed.indexOf(x)>=0)
          return x;
        return "";
      };
      
      let allowedHvacFlowMode = ["Auto", "ContinuousOn", "PeriodicOn"];
      let parseHvacFlowMode = function(x) {
        if(allowedHvacFlowMode.indexOf(x)>=0)
          return x;
        return null;    
      };
      
      let serializeHvacFlowMode = function(x) {
        if(allowedHvacFlowMode.indexOf(x)>=0)
          return x;
        return "";
      };
      
      
      
      let vars = module.exports.vars = {
          TEMP: {
            id:0,
            title: "Temperature",
            parse: parseF,
            serialize: serializeF,
            type: "numeric",
            unit: {I: "°F", M: "°C"},
          }, 
          HUM : {
            id:1,
            title: "Humidity",
            parse: parseP, 
            serialize: serializeP,
            type: "numeric",
            unit: {I: "%", M: "%"},
            min:0,
            max:100
          },
          BINARY : {
            id:2,
            title: "Binary value",
            opts: { 
              1: {
                title: "On",
                icon: "binary-1.png"
              }, 
              0: {
                title: "Off",
                icon: "binary-0.png"
              }
            },
            type: "binary",
            parse: parseB, 
            serialize: serializeB,
          },
          PERCENTAGE : {
            id:3,
            title: "Percentage",
            parse: parseP, 
            serialize: serializeP,
            unit: {I: "%", M: "%"},
            type: "numeric",
            min: 0,
            max: 100,
          },
          PRESSURE : {
            id:4,
            title: "Pressure",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "hPa", M: "hPa"},
            decimals: 1,
            type: "numeric",
          }, 
          FORECAST : {
            id:5,
            title: "Forecast",
            opts: { 
              stable: { 
                icon: "forecast-stable.png",
                title: "Stable"
              }, 
              sunny: { 
                icon: "forecast-sunny.png",
                title: "Sunny"
              }, 
              cloudy: { 
                icon: "forecast-cloudy.png",
                title: "Cloudy"
              }, 
              unstable: { 
                icon: "forecast-unstable.png",
                title: "Unstable"
              }, 
              thunderstorm: { 
                icon: "forecast-thunderstorm.png",
                title: "Thunderstorm"
              }, 
              unknown: { 
                icon: "forecast-unknown.png",
                title: "Unknown"
              }, 
            },
            parse: parseForecast,
            serialize: serializeForecast,
            type: "text",
          },
          RAIN : {
            id:6,
            title: "Rain",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "in", M: "mm"},
            decimals: 1,
            type: "numeric",
          },
          RAINRATE : {
            id:7,
            title: "Rain Rate",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "in/h", M: "mm/h"},
            decimals: 1,
            type: "numeric",
          },
          WIND : {
            id:8,
            title: "Wind",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "mph", M: "m/s"} ,   // Knots?
            decimals: 1,
            type: "numeric",
          },
          GUST : {
            id:9,
            title: "Gust",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "mph", M: "m/s"},   
            decimals: 1,
            type: "numeric",
          },
          DIRECTION : {
            id:10,
            title: "Direction",
            parse: parseF,        // Should come in as degrees from north 
            serialize: serializeF,
            unit: {I: "°", M: "°"},
            type: "numeric",
          },
          UV : {
            id:11,
            title: "UV Level",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          WEIGHT : {
            id:12,
            title: "Weight",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "lb", M: "kg"},
            decimals: 1,
            type: "numeric",
          },
          DISTANCE : {
            id:13,
            title: "Distance",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "in", M: "cm"},
            type: "numeric",
          },
          IMPEDANCE : {
            id:14,
            title: "Impedance",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "Ω", M: "Ω"},
            type: "numeric",
          },
          ARMED : {
            id:15,
            title: "Armed Status",
            parse: parseB, 
            serialize: serializeB,
            type: "binary",
          },
          TRIPPED : {
            id:16,
            title: "Tripped Status",
            opts: { 
              1: { 
                title: "Tripped",
                icon: "door-1.png"
              }, 
              0: {
                title: "Untripped",
                icon: "door-0.png"
              }
            },
            parse: parseB, 
            serialize: serializeB,
            type: "binary",
            sensors: {
              DOOR: {
                opts: { 
                  1: { 
                    title: "Open",
                    icon: "door-1.png"
                  }, 
                  0: {
                    title: "Closed",
                    icon: "door-0.png"
                  }
                }
              },
              MOTION: {
                opts: { 
                  1: { 
                    title: "Motion",
                    icon: "motion-1.png"
                  }, 
                  0: {
                    title: "No Motion",
                    icon: "motion-0.png"
                  }
                }
              },
              SMOKE: {
                opts: { 
                  1: { 
                    title: "Fire!",
                    icon: "smoke-1.png"
                  }, 
                  0: {
                    title: "No Fire",
                    icon: "smoke-0.png"
                  }
                }
              }
            }
          },
          WATT : {
            id:17,
            title: "Watt",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "W", M: "W"},
            decimals: 0,
            default: 0,
            type: "numeric",
          },
          KWH : {
            id:18,
            title: "Energy Used",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "kWh", M: "kWh"},
            decimals: 1,
            type: "numeric",
          },
          SCENE_ON : {
            id:19,
            title: "Scene On",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          SCENE_OFF : {
            id:20,
            title: "Scene Off",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          HVAC_FLOW_STATE : {
            id:21,
            title: "HVAC Flow State",
            opts: { 
              Off: { title: "Off"}, 
              HeatOn: { title: "Heat On"},
              CoolOn: { title: "Cool On" },
              AutoChangeOver: { title: "Auto" }
            },
            parse: parseFlowState,
            serialize: serializeFlowState,
            type: "text",
          },
          HVAC_SPEED : {
            id:22,
            title: "HVAC Speed",
            parse: parseHvacSpeed, 
            serialize: serializeHvacSpeed,
            opts: {
              Min: { title: "Min"}, 
              Normal: { title: "Normal"}, 
              Max: { title: "Max"}, 
              Auto: { title: "Auto"}  
            },
            type: "text",
          },
          LIGHT_LEVEL : {
            id:23,
            title: "Light Level",
            parse: parseP, 
            serialize: serializeP,
            unit: {I: "%", M: "%"},
            type: "numeric",
            min: 0,
            max: 100,
          },
          VAR1 : {
            id:24,
            title: "Var 1",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          VAR2 : {
            id:25,
            title: "Var 2",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          VAR3 : {
            id:26,
            title: "Var 3",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          VAR4 : {
            id:27,
            title: "Var 4",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          VAR5 : {
            id:28,
            title: "Var 5",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          UP : {
            id:29,
            title: "Up",
            command: true,
            parse: parseS, 
            serialize: serializeS,
            type: "action",
          },
          DOWN : {
            id:30,
            title: "Down",
            parse: parseS, 
            serialize: serializeS,
            type: "action",
          },
          STOP : {
            id:31,
            title: "Stop",
            parse: parseS, 
            serialize: serializeS,
            type: "action",
          },
          IR_SEND : {
            id:32,
            title: "IR Send",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          IR_RECEIVE : {
            id:33,
            title: "IR Received",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          FLOW : {
            id:34,
            title: "Flow",
            parse: parseF,
            serialize: serializeF,
            sensors: {
              WATER: { unit: {I: "gal/min", M: "l/s"} },
              GAS: { unit: {I: "scfm", M: "scm/h"} },
            },
            decimals: 1,
            type: "numeric",
          },
          VOLUME : {
            id:35,
            title: "Volume",
            parse: parseF,
            serialize: serializeF,
            sensors: {
              WATER: { unit: {I: "gal", M: "l"} },
              GAS: { unit: {I: "SCF", M: "Nm³"} },
            },
            type: "numeric",
          },
          LOCK_STATUS : {
            id:36,
            title: "Lock Status",
            parse: parseB, 
            serialize: serializeB,
            opts: { 
              0: {
                title: "Open",
                icon: "lock-0.png"
              },
              1: { 
                title: "Lock",
                icon: "lock-1.png"
              }, 
            },
            type: "binary",
          },
          LEVEL : {
            id:37,
            title: "Level",
            parse: parseF,
            serialize: serializeF,
            sensors: {
              AIR_QUALITY: { unit: { I: "mg/m³", M: "mg/m³"} },
              DUST: { unit: {I: "mg/m³", M: "mg/m³"} }, 
              SOUND: { unit: {I: "dB", M: "dB"} }, 
              VIBRATION: { unit: {I: "Hz", M: "Hz"} }, 
              LIGHT_LEVEL: { unit: {I: "Lux", M: "Lux"} }, 
              MOISTURE: { unit: {I: "", M: ""} },
            },
            decimals: 1,
            type: "numeric",
          },
          VOLTAGE : {
            id:38,
            title: "Voltage",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "V", M: "V"},
            decimals: 1,
            type: "numeric",
          },
          CURRENT : {
            id:39,
            title: "Current",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "A", M: "A"},
            decimals: 1,
            type: "numeric",
          },
          RGB : {
            id:40,
            title: "RGB Color",
            parse: parseRGB,
            serialize: serializeRGB,
            type: "rgb",
          },
          RGBW : {
            id:41,
            title: "RGBW Color",
            parse: parseRGBW,
            serialize: serializeRGBW,
            type: "rgbw",
          },
          ID : {
            id:42,
            title: "Id",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          UNIT_PREFIX : {
            id:43,
            title: "Unit Prefix",
            parse: parseS, 
            serialize: serializeS,
            type: "text"
          },
          HVAC_SETPOINT_COOL : {
            id:44,
            title: "HVAC Setpoint Cool",
            parse: parseP, 
            serialize: serializeP,
            unit: {I: "°F", M: "°C"},
            decimals: 1,
            type: "numeric", 
            min:0, 
            max:100, 
          },
          HVAC_SETPOINT_HEAT : {
            id:45,
            title: "HVAC Setpoint Heat",
            parse: parseP, 
            serialize: serializeP,
            unit: {I: "°F", M: "°C"},
            decimals: 1,
            type: "numeric", 
            min:0, 
            max:100, 
          },
          HVAC_FLOW_MODE : {
            id:46,
            title: "HVAC Flow State",
            parse: parseHvacFlowMode, 
            serialize: serializeHvacFlowMode,
            opts: {
              Auto: {title: "Auto"}, 
              ContinuousOn: { title: "Continuous On"}, 
              PeriodicOn: {title: "Periodic On"} 
            },
            type: "text",
          },
          TEXT : {
            id:47,
            title: "Text",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          CUSTOM : {
            id:48,
            title: "Custom",
            parse: parseS, 
            serialize: serializeS,
            type: "text",
          },
          POSITION : {
            id:49,
            title: "Position",
            parse: parsePos, 
            serialize: serializePos,
            type: "map",
          },
          IR_RECORD : {
            id:50,
            title: "IR Record",
            parse: parseI,        
            serialize: serializeI,
            type: "numeric",
          },
          PH : {
            id:51,
            title: "PH",
            parse: parseF,
            serialize: serializeF,
            decimals: 1,
            type: "numeric",
          },
          ORP : {
            id:52,
            title: "ORP",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "mV", M: "mV"},
            decimals: 1,
            type: "numeric",
          },
          EC : {
            id:53,
            title: "EC",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "μS/cm", M: "μS/cm"},
            decimals: 1,
            type: "numeric",
          },
          VAR : {
            id:54,
            title: "Reactive Power",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "VAR", M: "VAR"},
            decimals: 1,
            type: "numeric",
          },
          VA : {
            id:55,
            title: "Apparent Power",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "VA", M: "VA"},
            decimals: 1,
            type: "numeric",
          },
      
          POWER_FACTOR : {
            id:56,
            title: "Power Factor",
            parse: parseF,
            serialize: serializeF,
            unit: {I: "", M: ""},
            decimals: 1,
            type: "numeric",
          },
      
          // Internal variables used by web interface
          I_BATTERY_LEVEL : {
            title: "Battery Level",
            id: 1000,
            parse: parseP,
            serialize: serializeP,
            unit: "%",
            type: "numeric",
          }, 
      };
      
      1 Reply Last reply
      0
      • AndyDHillA AndyDHill

        In the Serial API pages we can find a table that show the correlation, between type of sensor and data structure that can be used , eg a switch will be of type S_Binary and S_ Binary has elements of V_Status & V_Watt so far all well and good. But when we come to write C code be should be disciplined in our data type and we should be typecasting or ensuring that the data we pass into V_Watt is correct type w shouldn;t be passing in a DINT when V_Watt if it is defined as type Real (float), Similarly we should only pass Bool into S_Status if it is defined as bool, but it could be defined as sint?

        This leads to my Question, is there a table showing the data types (Bool, Float, Int, Din, Sint etc) for each of these elements. ?

        Type		                  Data Type 
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        V_TEMP		                     ?INT
        V_HUM		                     ?INT
        V_STATUS		             ?BOOL
        V_PERCENTAGE		             ?INT
        V_PRESSURE		             ?INT
        V_FORECAST		             ??????
        V_RAIN		
        V_RAINRATE		
        V_WIND		
        V_GUST		
        V_DIRECTION		
        V_UV		
        V_WEIGHT		
        V_DISTANCE		
        V_IMPEDANCE		
        V_ARMED		
        V_TRIPPED		
        V_WATT		
        V_KWH		
        V_SCENE_ON		
        V_SCENE_OFF		
        V_HVAC_FLOW_STATE		
        V_HVAC_SPEED		
        V_LIGHT_LEVEL		
        V_VAR1		
        V_VAR2		
        V_VAR3		
        V_VAR4		
        V_VAR5		
        V_UP		
        V_DOWN		
        V_STOP		
        V_IR_SEND		
        V_IR_RECEIVE		
        V_FLOW		
        V_VOLUME		
        V_LOCK_STATUS		
        V_LEVEL		
        V_VOLTAGE		
        V_CURRENT		
        V_RGB		
        V_RGBW		
        V_ID		
        V_UNIT_PREFIX		
        V_HVAC_SETPOINT_COOL		
        V_HVAC_SETPOINT_HEAT		
        V_HVAC_FLOW_MODE		
        V_TEXT		
        V_CUSTOM		
        V_POSITION		
        V_IR_RECORD		
        V_PH		
        V_ORP		
        V_EC		
        V_VAR		
        V_VA		
        V_POWER_FACTOR		
        
        
        

        So can anyone complete the above table ?

        K Offline
        K Offline
        kimot
        wrote on last edited by
        #3

        @AndyDHill
        Look at "MockMySensors" sketch in MySensors examples. Maybe it helps with some types.

        1 Reply Last reply
        0

        Hello! It looks like you're interested in this conversation, but you don't have an account yet.

        Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

        With your input, this post could be even better 💗

        Register Login
        Reply
        • Reply as topic
        Log in to reply
        • Oldest to Newest
        • Newest to Oldest
        • Most Votes


        16

        Online

        12.0k

        Users

        11.2k

        Topics

        113.4k

        Posts


        Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
        • Login

        • Don't have an account? Register

        • Login or register to search.
        • First post
          Last post
        0
        • MySensors
        • OpenHardware.io
        • Categories
        • Recent
        • Tags
        • Popular