Skip to main content

A Tiny Webserver I use for icons and logging for my Hubitat Controlled ERV project

 I use this for logging from inside a rule, using an HTTP GET, for saving data from Maker API to disk on a Windows host.

I will probably add emailing to this app soon so I can send the files as attachments to IFTTT and have them save the attachment to Google drive, I imagine.

Here is the rule.














C# Code Here





And the possibility of using the maker API to run real-time graphs and gauges like in Node-Red.

I'm working on a wee project 2 building an ERV system using my Hubitat, Arduino and Xiaomi.

Usage

HTTP call in rule machine

http://192.168.1.71:82/logtempertures.svs?csv=Outside%20Climate,Hallway%20Climate,Bedroom%201,Bedroom%202,Bathroom%20Climate,Room%20Climate,Office%20Climate

You can see how the above URL works ?csv=name of device 1, name of device 2

?csv=name of device 1, name of device 2

Makes the logger log a file on the Windows host for the whole day, and return all the rows currently in the file.

?json=name of device 1, name of device 2

Makes a log for the yyyy-MM-dd-hh-mm-ss

The file name is logNAME-yyyy-MM-dd.txtx for CSV data and logNAME-yyyy-MM-dd-hh-mm-ss.json for the JSON format.

As long as the call begins with log and has the .svs extension it will work.





if you use http://192.168.1.71:82/logtempertures.svs?json=Outside%20Climate, for example, you get back JSON










The JJSON with the current device value and is stored here, a new file for every second.




You need to set the Get All Devices URL e.g. http://192.168.1.65/apps/api/6/devices?access_token=???????????????????? to point to your Hub in App.Config


settings1732×326 29.4 KB


A JavaScript usage example

	$.getJSON('http://192.168.1.71:82/logtempertures.svs?json=Outside%20Climate', function(json){
	
		console.log(json[0].attributes)
	
		var val = json[0].attributes[6].currentValue;

		console.log(json[0].val)
	
	});


The C# that does the work

     static void HTTPserver_SVSrequest(object o, HTTPrequest Request)
    {

        if (Request.Action != null)
            Console.WriteLine("Action: " + Request.Action.Trim());

        if (Request.Args != null)
            Console.WriteLine("Post Args: " + Request.Args);

        if (Request.Action.Trim().ToLower().EndsWith(".svs"))
        {
            if (Request.Action.ToLower().StartsWith("log"))
            {
                JArray deviceJSON = new JArray();

                //Request.RawURL
                //"/LogClimate.svs?ids=Hallway%20Climate,Outside%20Climate"

                string args = "";

                if (Request.Method.Trim() == "POST")
                    args = Request.Args.TrimEnd('#').Replace("%20", " ");
                else if (Request.Method.Trim() == "GET")
                    args = Request.RawURL.Replace("%20", " ");

                string[] dlabs = args.Split('=')[1].Split(',');

                using (WebClient wc = new WebClient())
                {
                    string url = Settings.Default.GetAllDevicesURL;

                    string sjson = wc.DownloadString(url);

                    DeviceList = JArray.Parse(sjson);

                }

                string CSVline = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") + ",";

                //Each ID
                foreach (string dlab in dlabs)
                {
                    //Find Device
                    foreach (JObject jo in DeviceList)
                    {                            
                        /*
                        {
                        "id":"673",
                        "name":"Xiaomi Temperature Humidity Sensor",
                        "label":"Hallway Climate"
                        },
                        */
                        if (jo["label"].ToString() == dlab)
                        {
                            string did = jo["id"].ToString();

                            CSVline += ("device," + dlab + ",ld," + did + ",");

                            using (WebClient wc = new WebClient())
                            {                                 
                                //e.g. http://192.168.1.65/apps/api/6/devices/673?access_token=ae6f8845-6916-4b8a-a9b2-67f7f5261ef7
                                string url = string.Format("http://192.168.1.65/apps/api/6/devices/{0}?access_token=ae6f8845-6916-4b8a-a9b2-67f7f5261ef7", did);
                                string sjson = wc.DownloadString(url);

                                JObject jod = JObject.Parse(sjson);

                                deviceJSON.Add(jod);
                                /*
                                "attributes":[
                                    {
                                    "name":"battery",
                                    "currentValue":70,
                                    "dataType":"NUMBER"
                                    },
                                    {
                                    "name":"batteryLastReplaced",
                                    "currentValue":"Jul 20 2020",
                                    "dataType":"STRING"
                                    },
                                    {
                                    "name":"humidity",
                                    "currentValue":31.9,
                                    "dataType":"NUMBER"
                                    },
                                    {
                                    "name":"lastCheckinEpoch",
                                    "currentValue":"1601591927215",
                                    "dataType":"STRING"
                                    },
                                    {
                                    "name":"lastCheckinTime",
                                    "currentValue":"Oct 2, 2020 11:38:47 AM",
                                    "dataType":"DATE"
                                    },
                                    {
                                    "name":"pressure",
                                    "currentValue":null,
                                    "dataType":"NUMBER"
                                    },
                                    {
                                    "name":"temperature",
                                    "currentValue":23.79,
                                    "dataType":"NUMBER"
                                    }
                                ],
                                */
                                string rejectAttr = "lastCheckinTime,lastCheckinEpoch,batteryLastReplaced";

                                JArray attrs = JArray.Parse(jod["attributes"].ToString());

                                foreach(JObject attr in attrs)
                                    if(!rejectAttr.Contains(attr["name"].ToString()) )
                                        CSVline += (attr["name"].ToString() + "," +  attr["currentValue"].ToString() + ",");
                                
                            }//End of using (WebClient wc = new WebClient())

                            break;//Whole device done

                        }//End of if (jo["label"].ToString() == dlab)

                    }//End of foreach (JObject jo in DeviceList)

                }//End of  foreach (string dlab in dlabs)

                CSVline = CSVline.TrimEnd(',');

                //WRITE AS JSON
                if (args.Contains("json"))
                {
                    string folder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + @"\json\";

                    if (!Directory.Exists(folder))
                        Directory.CreateDirectory(folder);

                    string file = folder + Request.Action.Replace("/", "\\") + "-" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss") + ".json";

                    string sjson = JsonConvert.SerializeObject(deviceJSON);

                    File.WriteAllText(file, sjson);
                    
                    //Send back last value usefull
                    HTTPserver.Response(sjson);
                    
                }
                //WRITE AS CSV TEXT
                else if (args.Contains("csv"))                    
                {
                    string folder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + @"\data\";

                    if (!Directory.Exists(folder))
                        Directory.CreateDirectory(folder);

                    string file = folder + Request.Action.Replace("/", "\\") + "-" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";

                    //Add this line
                    File.AppendAllText(file, CSVline + "\r\n");

                   //Get whole file so far
                    string CSVlines = File.ReadAllText(file);

                    HTTPserver.Response(CSVlines);
                } 

            }//End of if (Request.Action.ToLower().StartsWith("logclimate"))

        }//End of else if (Request.Action.Trim().ToLower().EndsWith(".svs"))
    }

Hubitat I may be missing something, but if not, when are you going to add some JSON handling into your marvellous product, please?

Popular Posts

The Heat Exchanger

  Under construction It took around 6 hours to layer the core Based on a 60 Litre Sistema box with a 400 X 400 X 350 6 mm Coroplast core. The fan speed controller is mounted on the end so it takes a 12V 10 Amp supply from the power system and 2 analogue 0 to 5 V signals for speed demand. The pink foam is a NZ$12 exercise matt which insulates it for both sound and heat quite nicely. 60 Litre Plenum on the vent side See  https://greenwayerv.blogspot.com/2020/10/we-added-60-litre-plenum-on-vent-side.html

Home Ventilation Heat Exchanger

 

The Experiment

First things first an experiment. I had been looking around at various homemade ERV core designs and eventually decided to try a Coroplast design. The experimental core was tiny 12 X 12 X 12 cm  using 3 mm Coroplast Nice isn't it :) 20 80 mm PC cooling fans at a fixed speed four temperature probes. Here is the full scale one around 40 X 40 X 35 cm using 6 mm Coroplast A Wemos D1/R2 for instrumentation sending temperatures via MQTT to Node-Red. I'm was not totally convinced by the numbers but it did definitely exchange significant amounts of heat. I checked the calibration and it was good compared to my multi-meter TC probe. In any case it seems to work even at such a small scale. Never did understand why it seems to gain more heat that it lost from the exhaust steam, checked for leaks there were none. Any way  a hairdryer experiment showed it worked and heat was transferred too so I was happy that it would probably work at a larger scale.

Heat Exchanger Low Ambient 86% Heat Recovery

  Drop on exiting air 2.9C Heating of incoming air 2.5C So 86% of the heat recovered

The ERV Data Acquisition System

  Progress over the weekend with the ERV DAS The 999's are me testing the sensor disconnected response. I have a Pitot tube left over from an RC plane I think I might put it in the inlet air stream Pin assignment in the Arduino sketch DHT dht[ 4 ] = {DHT( 4 , DHTTYPE),DHT( 0 , DHTTYPE),DHT( 2 , DHTTYPE),DHT( 14 , DHTTYPE)}; There are 4 DHT22's inside heat exchanger lid, shown below. Sensor locations Arduino code #include < Arduino_JSON . h > #include < ESP8266WiFi . h > #include < MQTT . h > #define LED_PIN LED_BUILTIN //LED_BUILTIN is built in LED #include "DHT.h" // Uncomment whatever type you're using! #define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //#define DHTTYPE DHT21 // DHT 21 (AM2301) WiFiClient net ; MQTTClient MQTTclient ; const char ssid [ ] = "Network" ; const char pass [ ] = "DF@#$%" ; // the IP address for the MQTT server char MQTTip [

Drivers and Arduino Sketches for ERV Fans Control Testing

  The Hookup For proof of concept seems to run reliably in the lab. I don't want to be up and down ladders to make update I intend keeping the control system in Hubitat so I can tinker to my Hearts content. Boost fan PWM control as well as start relays. The valve position is a separate driver and device in Hubitat, This will use one of the PWM Four boost fans in the ducts for each room inlet. One fan in the two way turbo exhaust valve. The valve position is a separate driver and device in Hubitat, This will use one of the PWM pins on the Wemos and its analogue input for the position pot Hubitat Valve Position Driver import groovy.json.JsonSlurper metadata { definition(name: "Greenway ERV Turbo Valve", namespace: "Greenway", author: "Nick Goodey") { capability "Initialize" capability "Switch" capability "Switch Level" command "on" command "off" } preferences { section("Device&qu