A failed attempt to use Google APIs to Manage a building's Heating System

Introduction

It's always nice to be able to describe experience on a successful project. But it's useful to record failure as well. This way others may avoid the "stopper", and there's always the possibility that someone may be able to suggest a way of fixing things.

Here's a failed project though. Why might you want to read about it? Well:

  • You might be interested in something a bit different from conventional data-processing.
  • You might be interested in using Google apis
  • You might even be interested in heating a building intelligently!

The Problem

Where I live in the UK, the focus of community life is our Village Hall. This hosts regular social activities like coffee mornings, toddler groups, badminton clubs and film nights etc. Basically it's just a large, high, room with an attached kitchen and loos. It's heated by a ramshackle combination of air-source air-conditioners and an oil-fired boiler. Some of this kit has actually failed and the rest is overdue for replacement. So, while the present pandemic has put a brake on many activities, we're taking the opportunity to sort things out. Here's a picture of our fine Hall so you can get an idea of the scale of things.

The main considerations are reliability and fuel costs. The advice we've had is that we should focus on replacing the obsolete oil-fired boiler to provide both heating and hot water and to retain the air-conditioners as backup. Where I got interested in the project was that it was also suggested that, in order to minimize fuel usage, we should consider some sort of automated management system that ensures we only heat the hall when we need to.

Because we use Google calendars to record bookings we already have digitised information about when our hall is in use. As an IT enthusiast I was pretty confident that I could write some software to read these calendars by deploying Google's "user-facing" client apis. I thought I might then be able develop this arrangement further to control the hall's heating via a "Nest" thermostat, one of the "smart home" heating devices that Google markets under the "Nest" brand.

Possible solutions

I know enough about process-control technologies to realise that this would be a very unconventional arrangement. A professional heating engineer would install some sort of specialist micro-processor locally. But I was entranced by the chance to play with the Google apis and on the rebound from a diy garden project involving an arduino and a solar panel. This had been very entertaining, but only if you enjoyed fooling with temperamental electronics and primitive software.

So basically I knew I probably wasn't going to get very far with this but I thought it would be interesting to give it a go.

The first problem was to decide on some "architecture" for the control program. This has to be some sort of process that runs "regularly" checks on the "state" of its sensors and issues actions as necessary to it various control devices. Options I considered were:

  • A remote server-based node.js process initiated via a schedule
  • A local browser based html/javascript process launched at start-up and anchored on a browser tab
  • A local node.js process launched at start-up.

In practice, I quickly realised that the first option would require me to acquire a VPS account of some sort. This was going to cost more than I was prepared to pay, so I was left looking at the local server options.

The local server options both share the idea that, rather than relying on a scheduler to perform a process-control "tick", it was better to get the system running, send it sleep and then wake it up every so often to check its state (say every minute or so). This way I wouldn't have to add the management of a temperamental local scheduler to my difficulties.

Because most of my recent experience has involved html/javascript running in a browser app, this is where I started.

What could possibly go wrong?

Browser solution

Google publishes a huge range of apis for javascript clients - see https://developers.google.com/apis-explorer/#p/ - and both calendars and smart devices are included in the list. Even better, the apis are nicely documented and supported with "quickstart" example code to give you a bit of confidence. In truth you do need this because while the apis themselves are pretty easy to use, access is through a OAuth2.0 security framework that requires you to register a "project", enable your apis and obtain your a OAuth2.0 "client id" and "client Secret". All of this involves much bureaucracy and the negotiation of a mass of unfamiliar terminology.

Still, it wasn't too long before I'd got a browser app that "ticked" every minute, read my google calendar and fed this to a piece of code to perform some intelligent actions.

Here's the construct I used to handle the "ticking":

const tickLength = 1; //minutes
async function performTick() {
    applyIntelligentManagement();
    await sleep(tickLength * 1000); // sleep for tickLength minutes
    performTick();
}
performTick();

Here's the code I used to access my Google Calendar. It gets the current date time, inspects the calendar and places details of the next scheduled event in the program's global hallEvent object. The structure of this object mimics the relevant bit of the Google calendar item object documented at https://developers.google.com/calendar/api/v3/reference/events#resource.

async function getNextHallEvent() {
      const response = await gapi.client.calendar.events.list({
        'calendarId': ' --- my Calendar id --- ',
        'timeMin': (new Date()).toISOString(),
        'showDeleted': false,
        'singleEvents': true,
        'maxResults': 1
      })
      hallEvent.summary = response.result.items[0].summary;
      hallEvent.start.dateTime = adjustForConditions(response.result.items[0].start.dateTime);
      hallEvent.end.dateTime = response.result.items[0].end.dateTime;
    }

The adjustForConditions() function referenced above is used to apply a "warm-up" allowance to the start time for a scheduled event.

This parent getNextHallEvent() function itself is called by a complex block of OAuth2.0 code inherited verbatim from the Google quickstart that I was following and which, frankly, I understood only dimly. Suffice to say that this code had to be run from a server location that I had authorised via the Google console. On initial execution it required me, additionally, to authorise its use on my particular calendar.

Back on firmer ground directly, I augmented the performTick() function with a very entertaining piece of logic designed to turn heating on and off by toggling a Nest thermostat between a "coolHall" setting and a "warmHall" setting. This code is a bit verbose,so I won't inflict it on you here, but here's a general description.

As indicated earlier, the program wakes up every so often, looks at the clock and the start time of the next scheduled event. If the current time now falls within the time window for the event but "hasn't yet been started". it sets the thermo to "warmHall". The concept of "event not yet started" allows for the possibility of user intervention. Somebody might enter the Hall prior to the expected start-up time for the next event and turn the thermostat up manually. In this case, the logic interprets the action as creating an "impromptu meeting" that replaces its current hallEvent. Furthermore, while it must do nothing to override whatever setting it sees on the thermo, it needs to make a judgement about when the impromtu meeting is due to end otherwise the heating may be left on unnecessarily. Arbitrarily, it currently gives impromptu meetings a 2-hour elapse time. Similar discretion is required when a scheduled meetings has actually started. User actions to turn the thermostat up must be respected during a meeting, but once the scheduled end time is reached the heating must be turned down to coolHall.

Whenever the heating is turned down by the control logic, a new hallEvent is obtained from the Google calendar.

At this point I should say that, at this stage, while I was going through the motions of inspecting and adjusting the settings on a Nest Thermo I wasn't actually doing this - just calling dummy readNestThermo() and setNestThermo() functions. But my program ran happily, ticking every minute and handling test Events loaded into a test Calendar in a competent fashion. Once launched in a browser tab on a laptop configured to stay permanently powered, it seemed content to monitor a simulated Village Hall's heating requirements as specified in a Google Calendar for protracted periods without complaint. I'd added some logging so that I could monitor status and actions so things were looking fairly encouraging.

Then I turned to the Nest api.

From the outset, the quickstart for the Nest software atLink took a very different line from what I'd seen for the Calendar api. Whereas the latter had given me sample javascript to paste into a conventional html file that I could upload to my own server for execution, the nest api quickstart instructed me to clone a much more complicated stack of code from a github repository and follow procedures that would allow me to run it as a "Firebase" Project on Google servers. On the way I had to :

  1. Register a new project with the Google Cloud Platform and enable its use of the Smart Devices Api
  2. Register a new project with the Device Access Console 3.register a "Firebase project" in the Google Device Access Console ($5 registration fee)
  3. Use the Firebase Console to create a Firebase Project referencing the project created at step 1. With all this in place I then had to use Powershell to "deploy" my local copy of the Github code. Essentially this uploaded the software to the Google server and launched it as a tab in my browser. This entailed using npm to first install firebase tools so that I could initialise a Firebase hosting project (lots of questions to answer to configure this) that in turn would allow me to run the firebase deploy command to perform the upload. Surprisingly, at the end of all this, my browser displayed the following page:

This allowed me to login with my oauth2 credentials and, in principle, list my Nest Devices and control them. Yay!

In practice, while I could login successfully, I didn't actually at this stage possess any Nest devices so clicking "list devices" just displayed a screen saying "You don't have any Nest devices". Still, things looked promising.

Google's Nest product range includes cameras and doorbells as well as Thermostats, but they're all quite pricy (eg £170 for a thermo). Then I came across something called a Nest mini priced at around £25. This is an Alexa-style device that you can talk to and find out, for example, whether or not it's raining outside. Crucially, for my purposes it was supported by the Google Home app, so I bought one and found that I could indeed start and stop it using my mobile phone. Crushing disappointment followed, however, when I ran my Firebase project code again and was told that I still had no Nest Devices. Subsequent communications with Google's Nest support people confirmed that the Nest mini is not regarded as a Nest device and so the Smart Devices api doesn't support it. Hmm.

On a more positive note, discouraged by the complexities of the Powershell deploy procedure, I cannibalised the local code that I was feeding this and mounted it in the html code I'd developed for the Calendar project. Guess what - it worked. Or at least it still allowed me to login and display the "no devices" message.

Node solution

At this point I should really have given up as I was already $5 + £25 down and the only way forward was to pay Google another £170 so I could but a Nest thermostat. But the project was proving so entertaining that I thought I'd take another run at it using the third option listed in the "possible solutions" section above - node.js.

Although I knew roughly what node.js was about, I'd never felt the need to do much more than fire it up in Powershell. Since there's no native graphical user-interface and since I'd never felt the need to write server code I had never felt any need to take things further. Here was a chance to pick up some new skills. In any case, I felt instinctively that a browser tab running my html/javscript code "24*7" was not a sensible arrangement - Node would be a better choice.

Again there was plenty of helpful Google documentation to enable me to convert the original calendar-reading code to work in node. https://www.npmjs.com/package/googleapis describes an npm package that seems to contain the same list of apis available to the html version. Another quickstart at https://developers.google.com/calendar/api/quickstart/nodejs described the procedure for deploying these and it didn't take long at all to produce a node version of my code. Only two things delayed me - first, the object produced by the calendar.list method used to read my Google calendar took a different form in the new library and second I had to install axios to allow me to continue to make http calls to maintain my logging database. Debugging in Node was a new experience and the loss of the browser debugging tools was a serious shock at first. But the day was saved here when I lit on a primer for the VScode tools at https://itnext.io/the-absolute-easiest-way-to-debug-node-js-with-vscode-2e02ef5b1bad.

So, before too long, I had a Node version of my Browser project javascript ticking every minute in node and logging dummy Nest calls to my monitor database. Now, could I replace the dummy calls to the Nest thermo with real ones?

At this point I got a shock. When I looked at the branch of the java script node.js api for Smart Devices at https://github.com/googleapis/google-api-nodejs-client I found there was nothing actually there - just a note telling me that this was "only available to trusted partners"

Feeling distinctly foolish (since I should really have noted the "trusted partner" bit on the api's entry in the api list referenced above), I went back and had another look at the Javascript Quickstart used in the Browser project.

Tacked onto the very end of the Quickstart there is a section headed "Node.js Server (Optional)". Its introduction says this is for "those of you who want to build on the server side" and describes how you might construct "a proxy server that can redirect your requests from the browser".

It then describes a procedure that uses firebase in powershell to initialise a file structure in your project with a folder in which you will develop an index.js file to contain your "cloud functions", a package.json file to define the settings and a node_modules directory to contain the dependencies. The procedure also requires you to use npm to install libraries for express (a lightweight server framework) and xmlhttprequest.

The index.js file is actually initialised by the firebase procedure and the quickstart goes on to describe how you would amend this using the Express libraries so that it becomes a "servlet that listens for POST requests, transmit them to a destination URL specified in the payload, and responds with the response received from the transfer".

The end result is that "this will route URLs starting with /proxy to the Express server, and the rest will continue to go to the index.html". This makes a bit more sense when you are further instructed to "add a proxyRequest function in scripts.js" that, when called, directs your payload to the https://smartdevicemanagement.googleapis.com/v1 endpoint.

You are then told to use this function in the postThermostatMode() and postTemperatureSetpoint() functions within scripts.js instead of the original deviceAccessRequest(...) calls. Finally, you are instructed to use firebase to "deploy" the project so that scripts.js becomes a "Node.js proxy server using Express on Cloud Functions".

This left me quite mystified since the original deviceAccessRequest(...) calls used the same endpoint, so the "server" just seems to be doing exactly the same job as the code in the original browser-based. In what way was this arrangement helping anyone who "wants to work on the server side". And specifically, how could I use it to enable the node.js code I constructed earlier to deploy my calendar-controlled heating logic to make calls to the smart devices api?

Having thought this through a wee while I realised that if the browser app could use the proxy server, perhaps my node.js code could use it too. But the quickstart didn't seem to be inclined to offer any advice.

At this point I decided that life was going to be pretty unpleasant if I persisted with this line of development. Google clearly didn't want to make access to smart device easy for the casual developer.

Conclusion

The Google api's don't afford a practical solution to my Hall Heating Control requirement.

In a way I should count myself lucky that I got as far as I did. I have a web app that I think I could, in principle, deploy to run my heating-control logic in a browser tab. Because I don't actually possess a smart device, I haven't actually tested this but even if it worked, I now realise that it would be very reluctant to deploy it. Over the course of the project, several things have become clear to me:

  • As previously stated, a browser tab on a web-enabled PC doesn't feel like a sensible place to mount coded that is monitoring important remote processes.
  • Additionally, it began to dawn on me that nobody was going to thank me for bequeathing them a temperamental block of complex code that nobody but me (possibly, including me) really understood.

The problems I describe are not unique to the Google product range. In https://jedkirby.com/blog/hive-home-rest-api Jed Kirby describes his experiences into trying to obtain access to the Hive smart home devices. This is a similar product-set to the Nest devices but comes from British Gas. Jed describes his surprise that there is no Public Facing Rest API for these devices but explains how he was, nevertheless, able to hack his way in. I followed his instructions but can confirm that the loophole he uncovered no longer works. So there is another suite of smart home products that their manufacturers would prefer to keep firmly out of the hands of the casual developer.

One can see why - the opportunities for mischief are legion and the defences required to contain the risks make use by the casual developer impractical.

So, my project had failed. It had been great fun and had taught me a lot but my proposed solution was impractical. But surely it still makes sense to try and control your heating costs by turning your heating on only when your premises are being used. So how should one try and do this?

In considering a revised approach, I now realise that two things had misled me - firstly my desire to use the excellent Google calendars as my source of scheduling information and secondly my unsatisfactory experience with microprocessor applications. Beyond this, though, I'd realised that any serious application had to be one with commercial support.

I think the starting point for a revised approach is to accept that the device running the control logic needs to be in the premises that it is controlling and which has direct access to the sensors and switches that feed it information and deliver its control functions. Too many things can go wrong if you're reliant on a web connection to administer your directives.

So this now looks to me like a case for process-control technologies - microprocessors and wired device. T

To cut a long story short, I finally did what I should really have done at the outset and googled "property management services heating control". I now know that there are companies out there that deliver exactly the service that I require - heating controlled by local web-enable microprocessors that operate on calendars maintained via smartphone apps. They sell them to you at modest prices and, if they go wrong they fix them.

So that's the way we now plan to go. Hey ho. One lives and learns!

One final thought is to speculate on where Google itself might be planning to take all this. It's clearly a "work in progress" and there is a curious gap in the Nest product range. There are no Nest switches - only third party product interfaces to allow you to turn a light on, say, by a voice command. I sense there may be development still to come because it now occurs to me that the Nest thermostat is itself a microprocessor - and quite a sophisticated one at that - web-enabled (to permit remote access to its heating schedules) and now programmed with an AI style "learning mode" to work out its own schedules if it doesn't get any explicit instructions from the home-owner. One wonders how this code is deployed and whether it might one day be possible to change it.....

25