Recently I have become interested in understanding the status and production of a small solar grid at my home. Briefly, a solar system is composed of three main components; panels that collect the sun, inverters that transform DC to AC and a device that communicates with the inverters over the power lines and uploads it to the hardware vendor’s website.
While the hardware vendor, in this case Enphase provides apps and websites for checking in, I wanted something more modern for my smart home aspirations, a conversational interface. To do this I leveraged Dasha.ai and Enphase’s free tier API.
Getting Enphase API Access
Head on over to https://developer.enphase.com you can fill out a quick form and get access to a developer account, the free tier gives you 10,000 requests a month, which is enough for what I plan on doing.
Setup Your Dasha Project
We need to get the tooling setup for Dasha, this should only take a minute. Go ahead and register with https://auth.dasha.ai/Account/Register. Once registered grab the command line interface from NPM and login.
Next we will install the Dasha Studio extension for VSCode this will enable syntax highlighting and enhanced debugging of Dasha script.
npm i -g "@dasha.ai/cli"
dasha account login
Once we are setup there is a convenient project to bootstrap our efforts. We can clone it from here: https://github.com/dasha-samples/blank-slate-app.
The Dasha blank-slate-app is a great way to get started, but there are a couple of changes that I want to make first. First, create a logs folder and in the index.ts file we will change the logging line to create a log file in this folder with the current time stamp so that we can go back and review these logs to gauge the affect of our changes.
The second thing that we want to do is include a .gitignore file. This will both keep our repository orderly and we can drop the logs which will eventually contain our Enphase userid, something we want to keep secret. I grabbed a default nodeJS .gitignore and added a couple of lines to it. When I was done it looked like this.
The final thing we need to do in this step is to test everything thing. To do this open a terminal in the directory you are working and run following command:
npm run start chat
After building the application you should be presented with the default prompt and be able to tell it yes or no. Now on the fun part teaching out bot how to help.
Teaching our Bot How to Respond
For this stage of the project I want to focus on a couple of rudimentary skills and later we can build additional skills. The first skills that I want are the ability to view the status of the components in the system (inverters and Envoy) and see much energy is produced. We need one additional ability and that is to collect from the user their Enphase identification number, this number is sent with each request and while there is an API call it feels more like a live support agent if our bot prompts the user for it and the start of the conversation.
We will start with the first step of the process which is providing the bot with our Enphase userid, but what is it? Well the documentation alludes to being able to find it in the user site. However, I could not so I grabbed it from the application authorization page, something we have to do anyway. The Enphase developer portal should give you link for your default app that you can paste in your browser and after authorizing it will provide the userid on the confirmation page.
Since all of our responses require the user_id, let’s teach our bot to ask for it. To do this we will change the greeting phrase mapping from it’s default to one that works for us.
When we change what is asked by the bot we should expect that we will receive a different user response. We handle this change by modifying the intents. We included some intents, some more repetitive to handle the entity extraction, but we will talk about this in a minute. You will also see that we excluded some intents, with the size of the training data I wanted to be sure that we didn’t over fit on “user id” or similar phrases.
Subsequently we made an intent if a user expresses they don’t know what their user id is. If we take a look at main.dsl file we will see that we are using two different functions #messageHasIntent and #messageHasData. messageHasIntent will fire when we classify the user intent as not having an id. The second requires something else, that entity we talked about later. Scrolling down to the bottom of intents.json you will see we have added training examples that show our bot how to recognize a user id. To be real robust we probably need some more examples, but using random 26 alpha numeric character ids I generated it seemed to hold up relatively well as it is. One thing to note is you need to include this in the intents portion and also label the same data in your intents otherwise it will fail to extract your intents.
The last step is to put the user id into a context we defined at the top of the main.dsl file, this will preserve the context so we can reference it later which is going to be important. We do this in the onexit block of the node.
Fetching External Data
Not everything our bot needs to know is defined in this code. In our case we need to reach out and fetch information for the user. This could come in many forms, but in this case it is simply a RESTful API.
The first function we want is a simple check in on the production of the system. After defining the proper intents to capture the user’s desire to get production information we need to define a node to grab the information and return it. You can see we need to define the function and calls in three places; first at the top of the main.dsl file, second we make the call in the do portion of the show_inventory node and finally the business logic is written in the method body of index.js.
The JS is straight forward, make an API call to the Enphase API, get the user’s system id. With that system id make another call and extract the daily production from the returned data and send that back to our bot.
All custom functions in Dasha have two parameters; args gives us what is passed in from the user and conv which includes the conversational context information. We will use args to pull the user’s id and grab our api key out of dotenv file to make sure it stays secret.
Once our bot gets the information needed they can report it through a phrase mapping. The text key refers to static text while id refers to the value assigned by our function and by combining them we achieve string interpolation.
Note: be sure to include your phrase mappings down in the macros, so that they can be found by the nodes elsewhere.
An alternative to doing string interpolation in the phrase mappings is to pre-process the text in the business logic and return it via a variable. This is exactly the technique we use for the function to find bad inverters. Otherwise the pattern is the same as we used previously to get the output.
Dasha makes building a bot straight forward. Using the API we can offload the effort of tuning models and maintaining complex local build systems. We can easily build entity extraction and classification models. The developer oriented approach to bot design gives us flexibility in how we prepare and present text to the user.
My original goal still stands and to make the information in my solar system more accessible, and we didn’t quite get there this time. To do still stands to incorporate remote access to the bot from SMS would be great, incorporate some more functionality from the Enphase API and clean up some functions to conserve API calls.