I have a mini-split system for both my heating and cooling, and there’s no thermostat. So a little after I moved in, I bought some one I could stick on a shelf and peek over at. It has a remote sensor, too, so I could easily see if I should open the door and crank the fan; oh, and where I live, the what the weather says is always about 10 degrees off. But I couldn’t see the damn thing, from across the room. The light only came on, if you pressed a button on the top. I’m lazy, so I made a quick solder job, to keep the light on. It’s not fancy, it just has a diffused led back light behind a HTN LCD. I actually like how it looks, it has a nice subtle blue glow to it. In any case, I severely underestimated how long 2 AA’s could power that lighting; it was about 2 weeks. So I printed a little C battery holder, and hacked at it some more; yet, I was defeated again. It just needs too much juice.

Since I’ve already invested in Homekit, I started looking at my options. Ugh. The nice ones are like $60+ or the cheap ones need some hub. I already have enough hubs and I don’t want to drop $200 for 3 temperature sensors. Sp I got to scheming. I could make something that uses a dev board, a temp sensor, and I can probably push it into Homeassisant, to expose it to my setup.

This isn’t my expertise, at all. First I spent way too much time figuring out what board I wanted. I was going to settle on an ESP8266, but I knew I wanted a way to charge the thing… I hate batteries, but especially disposable batteries, and I can’t plug all of them in. The expansion boards for charging a battery, or whatever they are called cost too much, on top of the board price, so I upped my game to an ESP32. I spent hours on Amazon trying to find what I want, for the right price. At this point, I probably should have just dropped the money for a pre-made one.

But I continued on. I bought one to try it out, then a temp sensor, based on a post I found, and eventually a battery. I didn’t want to throw money out the window, I didn’t know how far I would get. So my parts list ended up looking like this: The ESP32 ($9.49), the DHT11 temp sensors ($1.60 each), and the 1000 mAh batteries ($5.75 each). It brought me to a grand total of $16.84, for each sensor I wanted to make. I probably went overboard with the batteries, but like I said, I hate batteries, so I found some pretty beefy ones and skipped doing the math. So not too bad, I can live that that price. I don’t have a lot of electronics related gear, so it worked out pretty cool. I get more stuff to play with, like jumper wires and bread boards I ordered, and even if the price ends up being more, I still save money. I’m cool with that.

Getting the ESP32 to work right was such a pain in the ass. For one, I don’t know C; I’m a Javascript and C# kinda guy. I wasn’t doing anything fancy though, so I made it through. The libraries are what confuse me. Getting the right ones is such a pain in the ass, way worse than the mess that exists in Javascript. I underclocked it and all that jazz, it doesn’t need much power, and my plan was to just make it a server that Homeassistant could hit, but no no no

I was reading and these boards do best on battery when they can go to sleep. Ugh. Okay. We will make a damn web server. It can wake up, push it to the server, then go back to sleep. I found a great post on how to save battery and stole all their code. Making a server like that is easy as pie, but I needed a place to serve it up. I chose my RaspberryPi that hosts my Homeassisant setup. I already setup Portainer on it, so I can push docker images up. But I kind of hate modern devops, so I knew nothing about docker. I managed to get it working by bundling up my js and a dockerfile and telling Portainer to make an image from it. From there I had to expose the right port and finally… it works. I’ll post all the code for everything at the end of this by the way. But god I hate docker and all of this nonsense. I can’t wait for the next generation of devops, this one sucks ass. The only thing worse than docker has to be Cloudformation. Anyway…

I had my web server working and now I had to change all my code to make it wake up, push what it feels? the temperature is, and sleep for 15 minutes. It wasn’t too much of a problem until I got to actually sending the data to my server. The amount of HttpClients and RestClients that just didn’t work with my setup blows my mind. I didn’t care of the code was bad, I just wanted it to work, and I couldn’t get as much as an error code back. Apparently I was supposed to HttpClient and not HTTPClient… okay… fuck off with that noise. What a waste of time. I got it working eventually, then it stopped. Yeah, cheap bread boards and jumper cables. Fun. Those jumpers are going to become cables I solder on to things, at least they will be useful and reliable in that setting.

But Homekit didn’t pick the damn thing up. I posted on Github, with the info I thought was necessary for them to know – I figured it was some kind of bug with the aREST plugin I was using in Homeassistant. The first try, some guy was a dick and was like you have to fill out this form, blah blah blah. I gave him shit of course, he held strong. So I filled out a new ticket with the info they “needed”, it added almost nothing more relevant, except version numbers. One person was kind enough on the second ticket to tell me I could make it work, with a “customize” config. The other said tickets aren’t for support… and closed it. They also told me aEST doesn’t support Homekit, so I went back to the first guy and told him thanks for wasting my time and being a dick. Fuck him. Anyway, it worked… but it was assuming my readings were in C and converting them to F. I had to set they are measured in F in both aREST and my new “customize” config. Done and done. I’ll make sure to post that code, as well.

Listen, I get it, you support an open source project and a bunch of idiots come to you crying all the time. It sucks. But you signed up for it and when an actual professional comes to you with all of the relevant information you need, don’t give them shit. You just come off as an asshole, for no reason, asshole.

At this point I had the web server running and my little ESP32 pushing to it.

Now to the case. I couldn’t just have wires and junk all over the place, so I fired up Tinkercad. God, please don’t take any of my advice when it comes to making 3d models, I have no idea what I am doing, but I’ll share my process anyway. I couldn’t find anything useful that was pre-made, so I took out (and by that I mean bought some cheap) digital calipers. I started with a solid box, but I quickly changed my mind. I planned out how the mounting of the board would go and made the standoffs. Then I built a crude representation of how the battery would sit under the board and adjusted. Shit, I don’t want all this stuff for the “top” half, so I un-did that work and made hallow box that would accommodate everything, split it in half, then cloned it. Okay, now I put the standoffs in, and a cut out for the micro-usb charging port. I threw in some slits for cooling, for good measure. I have no idea how that stuff works, but I put some at the bottom, so they won’t be seen. On the top, I made a cutout for the sensor to poke out a little, away from the heat of the CPU, and another stand off for the one screw hole it has. So I thought on the bottom I’d shove the battery it, and screw the board on top, then on the top, I’d mount the temp sensor. Lastly, I put a few 1.5mm holes through all of the case to try to get a good grip for some m2 screws I was going to try and use. I hit print and…

I messed up

I screwed up a few things. Somehow I made it only big enough for the battery to fit, length-wise. The tolerances were too tight for the sensor to stick out, okay, easy fix. And the stand off for the sensor was too high. I don’t know how I did that, but whatever. Oh, right, the standoffs for the board were too short. I guess I made them line up with the bottom of the case, instead of sitting on top of the bottom of the bottom case… if that makes any sense :D

So I’m adjusting a few things and am trying again. I’ll finish this soon and make a few more. What a long process. I hope it’s worth it!

Thanks for reading and here’s all my code:

In Homeassistant’s configuration.yaml

homeassistant:
    unit_system: imperial
    customize:
        sensor.bedroom_temperature:
            device_class: temperature
            unit_of_measurementstring: '°F'
        sensor.bedroom_humidity:
            device_class: humidity
            unit_of_measurementstring: '%'
            
...

sensor:
  - platform: arest
    name: 'bedroom'
    resource: http://192.168.1.126:3000/bedroom
    monitored_variables:
      temperature:
        name: temperature
        unit_of_measurement: '°F'
      humidity:
        name: humidity

My awful Ardrino code:

#include <Arduino.h>
#include <WiFi.h>
#include "driver/adc.h"
#include <esp_wifi.h>
#include <esp_bt.h>
#include <ESPmDNS.h>
#include "DHT.h"
#include "wifipass.h"
#include <HTTPClient.h>

#define DHTTYPE DHT11
#define DHTPIN 4
DHT dht(DHTPIN, DHTTYPE);

String ROOM = String("bedroom");

#define DEEP_SLEEP_TIME 15
#define WIFI_TIMEOUT 10000

  
int main() {
    float h = dht.readHumidity();
    float f = dht.readTemperature(true);

    Serial.println(h);
    Serial.println(f);
    
    if (isnan(h) || isnan(f)) {
      Serial.println(F("Failed to read from DHT sensor!"));
      return 1;
    }
  
    String degrees = String(f);
    String humidity = String(h);

    HTTPClient http;
    String TempURL = String("http://192.168.1.126:3000/" + ROOM + "/temp/" + degrees);
    http.begin(TempURL);
    int statusCode = http.GET();
    Serial.println(statusCode);
    http.end();
    
    HTTPClient http2;
    String HumidityURL = String("http://192.168.1.126:3000/" + ROOM + "/humidity/" + humidity);
    http2.begin(HumidityURL);
    int statusCode2 = http2.GET();
    Serial.println(statusCode2);
    http2.end();

    Serial.println("Sent new data to server");

    Serial.print(TempURL);
    Serial.print(HumidityURL);

    return 0;
}

void setup() {
}


void connect(void) {
  Serial.begin(115200);
  
  Serial.print("Connecting to WiFi... ");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  unsigned long startAttemptTime = millis();

  while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT){
    delay(10);
  }

  if(WiFi.status() != WL_CONNECTED){
    Serial.println("FAILED");
    goToDeepSleep();
  }

  Serial.println("OK");
  Serial.println(WiFi.localIP());
  
  dht.begin();
}

void goToDeepSleep()
{
  Serial.println("Going to sleep...");
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  btStop();

  adc_power_off();
  esp_wifi_stop();
  esp_bt_controller_disable();

  esp_sleep_enable_timer_wakeup(DEEP_SLEEP_TIME * 60L * 1000000L);

  esp_deep_sleep_start();
}


void loop()
{
  connect();
  main();
  goToDeepSleep();
}

My sexy node server code:


const express = require('express')
const app = express()
const port = 3000


const data = {}


app.get('/:room/', (req, res) => {
    const room = req.params.room;
    const type = req.params.type;

    if (!data[room]) {
        res.status(400).send(`{
            "variables" : {
               "temperature" : 69,
               "humidity" : 69
            },
            "id" : "bedroom01",
            "name" : "${room}",
            "connected" : true
         }`)
        return
    }
    if (!data[room]['temp'] || !data[room]['humidity']) {
        res.status(400).send(`{
            "variables" : {
               "temperature" : 69,
               "humidity" : 69
            },
            "id" : "${room}01",
            "name" : "${room}",
            "connected" : true
         }`)
        return
    }

    res.status(400).send(`{
        "variables" : {
           "temperature" : ${data[room]['temp']},
           "humidity" : ${data[room]['humidity']}
        },
        "id" : "${room}01",
        "name" : "${room}",
        "connected" : true,
        "updatedAt": ${data[room]['updatedAt']}
     }`)
})

app.get('/:room/:type/:value', (req, res) => {
    const room = req.params.room;
    const type = req.params.type;
    const value = req.params.value

    if (type !== 'temp' && type !== 'humidity') {
        res.status(500).send('type must be temp or humidity')
    }

    if (!data[room]) { 
        data[room] = {}
    }

    data[room][type] = value
    data[room].updatedAt = new Date()

  res.send(`${room} - ${type} updated to ${value}`)
})


app.listen(port,'0.0.0.0', () => {
  console.log(`Temp sensor server app listening at http://localhost:${port}`)
})

And lastly my disgusting Docker file

FROM node:12

WORKDIR /usr/src/app

COPY *.* ./

RUN npm install

COPY . .

EXPOSE 3000
CMD [ "node", "index.js" ]