{"id":98,"date":"2022-02-27T13:30:48","date_gmt":"2022-02-27T11:30:48","guid":{"rendered":"https:\/\/ahiru.eu\/blog\/?p=98"},"modified":"2022-02-27T23:18:39","modified_gmt":"2022-02-27T21:18:39","slug":"creating-a-smart-thermostat-using-esp32openhabmosquittoapacheletsencrypt","status":"publish","type":"post","link":"https:\/\/ahiru.eu\/blog\/2022\/02\/27\/creating-a-smart-thermostat-using-esp32openhabmosquittoapacheletsencrypt\/","title":{"rendered":"Creating a smart thermostat using ESP32+openHAB+Mosquitto+Apache+letsencrypt"},"content":{"rendered":"\n<p>I wanted a smart thermostat for my village home, so that we can turn off the heating when we leave and turn it on a few hours before we&#8217;re due to arrive. Unfortunately this came with a lot of restrictions, which basically excluded almost all choices in the market currently:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Encryption<\/li><li>No <code>admin<\/code>\/<code>admin<\/code> vulnerabillities<\/li><li>Something that respects the GDPR<\/li><li>Something ideally open source, or at least that respects the GPL<\/li><li>Open protocol, so I don&#8217;t need to pollute my phone with yet another fishy app<\/li><li>Something that doesn&#8217;t depend on third party servers, otherwise I risk ending up with an expensive paperweight at a random company&#8217;s whim<\/li><\/ol>\n\n\n\n<p>The excellent folks at https:\/\/hestiapi.com\/ have a product that look like it&#8217;s checking all my boxes. Plus, it&#8217;s apparently a company from Athens! However, I eventually decided on a DIY solution based on one of my pre-existing servers. I&#8217;d install OpenHAB and MQTT on my server, and have an ESP32 on-site as the controller. The advantage of using OpenHAB and MQTT on a pre-existing server, as opposed to a RaspberryPi on-site, is that I don&#8217;t need to try to speak with a real person on my ISP&#8217;s tech support in order to convince them to give me a real IP address.<\/p>\n\n\n\n<p>This blog post will cover the installation of openHAB and MQTT on an existing Apache web server using letsencrypt.<\/p>\n\n\n\n<p>For the following instructions, assume a root shell on the server.<\/p>\n\n\n\n<p>First of all, I installed mosquitto on my Debian server:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">apt install mosquitto mosquitto-clients<\/pre>\n\n\n\n<p>Then I edited <code>\/etc\/mosquitto\/mosquitto.conf<\/code> to make it work with a username\/password and also my existing letsencrypt certificates, by adding these lines at the bottom:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>tls_version tlsv1.2<br>listener 8883<br>allow_anonymous false<br>password_file \/etc\/mosquitto\/users<br>certfile \/etc\/mosquitto\/certs\/fullchain.pem<br>keyfile \/etc\/mosquitto\/certs\/privkey.pem<br>cafile \/etc\/ssl\/certs\/DST_Root_CA_X3.pem<\/code><\/pre>\n\n\n\n<p>Now it&#8217;s referencing some files that don&#8217;t exist yet. First of all, we need to remove the existing <code>\/etc\/mosquitto\/certs <\/code>directory and symlink it to our <code>\/etc\/letsencrypt\/live\/example.com<\/code> directory. We also need to give the <code>mosquitto<\/code> user access to the certificates by adding it to the <code>ssl-cert<\/code> group. Feel free to ignore the README that says that the directory must be readable only by the <code>mosquitto<\/code> user &#8211; having it part of the <code>ssl-cert<\/code> group works just fine.<\/p>\n\n\n\n<p>We also need to create the <code>\/etc\/mosquitto\/users<\/code> file. We initially edit it by adding a list of usernames and passwords, one username per line, with a colon between usernames and passwords. Example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">jimmy:password<br>admin:letmein<\/pre>\n\n\n\n<p>We then encrypt the file using this command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mosquitto_passwd -U \/etc\/mosquitto\/users<\/pre>\n\n\n\n<p>Restart the mosquitto service:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/etc\/init.d\/mosquitto restart<\/pre>\n\n\n\n<p>And this part is ready. Next, we install openHAB. I installed the testing distribution:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">wget -qO - 'https:\/\/openhab.jfrog.io\/artifactory\/api\/gpg\/key\/public' | apt-key add -<br>echo 'deb https:\/\/openhab.jfrog.io\/artifactory\/openhab-linuxpkg testing main' | tee \/etc\/apt\/sources.list.d\/openhab.list<br>apt update<br>apt install openhab openhab-addons openjdk-11-jre<\/pre>\n\n\n\n<p>Now, openHAB runs its own web server on port 8080, and 8443 for SSL using self-signed certificates. We do not want to expose port 8080 to the public. Also, for SSL it&#8217;s using certificates in a different format than letsencrypt&#8217;s default, so we would theoretically need to convert the certificates every two months and restart the openHAB server. It&#8217;s easier to configure Apache to do a reverse proxy on a different port than the default 443, which we use for our own stuff. The <a href=\"https:\/\/community.openhab.org\/t\/oh3-apache-reverse-proxy-authentication-fails-unauthorized-access\/120578\/15\">example<\/a> that I had found online uses port 444 instead, but Firefox complains that this address is restricted. So let&#8217;s use port 1443 instead:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> &lt;VirtualHost *:1443&gt;\n        ServerName example.com\n        SSLEngine on\n        SSLCertificateFile \/etc\/letsencrypt\/live\/example.com\/fullchain.pem\n        SSLCertificateKeyFile \/etc\/letsencrypt\/live\/example.com\/privkey.pem\n        Header set Set-Cookie \"X-OPENHAB-AUTH-HEADER=1\"\n        ProxyPreserveHost On\n        ProxyPass \/ http:\/\/127.0.0.1:8080\/\n        ProxyPassReverse http:\/\/127.0.0.1:8080\/ \/\n        RequestHeader set X-Forwarded-Proto \"https\" env=HTTPS\n        Header add Authorization \"\"\n        RequestHeader unset Authorization\n        ErrorLog ${APACHE_LOG_DIR}\/openhab_error.log\n        CustomLog ${APACHE_LOG_DIR}\/openhab_access.log combined\n        &lt;Location \/&gt;\n                AuthType Basic\n                AuthName \"example.com 1443 \"\n                AuthUserFile \/etc\/openhab\/.passwd\n                Require valid-user\n        &lt;\/Location&gt;\n&lt;\/VirtualHost&gt;\n<\/pre>\n\n\n\n<p>Add the necessary <code>NameVirtualHost *:1443<\/code> and <code>Listen 1443<\/code> to <code>\/etc\/apache2\/ports.conf<\/code> and you&#8217;re ready.<\/p>\n\n\n\n<p>You will also notice that we&#8217;re password-protecting the webpage. We&#8217;ll explain the reason in a while. For now, just create the file in question:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">htpasswd -c \/etc\/openhab\/.passwd jimmy<\/pre>\n\n\n\n<p>and enter the password in the prompt.<\/p>\n\n\n\n<p>After this is done, restart Apache, point your browser towards <code>https:\/\/example.com:1443<\/code> and create the administrator&#8217;s username and password. You will also be prompted to install the MQTT module.<\/p>\n\n\n\n<p>After logging in as administrator, go to Settings -&gt; (under System Services) API Security, click &#8220;Show advanced&#8221;, and enable &#8220;Allow Basic Authentication&#8221; in order for the Android app to work. (I&#8217;m not 100% sure that this step is necessary, in fact)<\/p>\n\n\n\n<p>Note: DO NOT disable &#8220;Implicit User Role&#8221;, as the Android app will break. It does ask for a username and password, but I think those are used for Apache&#8217;s authentication instead. I had initially tried to disable Apache&#8217;s authentication and also disable &#8220;Implicit User Role&#8221;, thinking that already gives me proper access control. The Android app failed spectacularly.<\/p>\n\n\n\n<p>Now, let&#8217;s add a dummy thermostat. Go to Settings -> Things and click the Plus button to create a new Thing. From MQTT Binding, select MQTT Broker. Add your <code>example.com<\/code> hostname (ideally not <code>127.0.0.1<\/code> otherwise certificate verification will fail), port <code>8883<\/code> even though it&#8217;s the default, provide the username and password you configured for mosquitto, and enable Secure Connection. Your broker should show up as Online. In order to prevent it from breaking at every letsencrypt update, disable Certificate Pinning and Public Key Pinning, and clear their hashes.<\/p>\n\n\n\n<p>For now, let&#8217;s add a dummy On\/Off switch. Go back to Things and add a new Generic MQTT Thing. Give it a name, select the MQTT Broker you added earlier, and then go to Channels. Add a Channel of On\/Off Switch type. Give it a name and select an MQTT State Topic, for instance <code>thermostat\/status<\/code>. Leave the Command Topic empty for now, it can be a read-only switch. Its Custom On value can be 1 and its Custom Off value can be 0. It should also show up as Online.<\/p>\n\n\n\n<p>Go back to Settings and click Items. Add an item for the switch you just added and select its channel. Let&#8217;s send a dummy command to turn it on:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mosquitto_pub --insecure -u jimmy -d -h example.com -p 8883 -t thermostat\/status -m 1 -P password<\/pre>\n\n\n\n<p>It should show up as ON on the openHAB GUI. Change 1 to 0 to turn it off.<\/p>\n\n\n\n<p>If you want to check if the command arrived to the Mosquitto server itself,<br>you can run a listener:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mosquitto_sub --insecure -u jimmy -d -h example.com -p 8883 -t thermostat\/status -P password<\/pre>\n\n\n\n<p>While it&#8217;s running, it should show you any updates that it catches.<\/p>\n\n\n\n<p>Note that I used the <code>--insecure<\/code> switch in both commands. I couldn&#8217;t get certificate verification to work here, but it doesn&#8217;t matter because it&#8217;s running on the host itself.<\/p>\n\n\n\n<p>You can also install the openHAB Android client and configure it with the <code>https:\/\/example.com:1443<\/code> remote server with your configured username and password. It will show an empty layout, but we haven&#8217;t configured our smart home&#8217;s layout yet. It will be explained in Part 2, together with the actual thermostat&#8217;s ESP32 implementation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I wanted a smart thermostat for my village home, so that we can turn off the heating when we leave[&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-98","post","type-post","status-publish","format-standard","hentry","category-english"],"_links":{"self":[{"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/posts\/98","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/comments?post=98"}],"version-history":[{"count":7,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/posts\/98\/revisions"}],"predecessor-version":[{"id":105,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/posts\/98\/revisions\/105"}],"wp:attachment":[{"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/media?parent=98"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/categories?post=98"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ahiru.eu\/blog\/wp-json\/wp\/v2\/tags?post=98"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}