Energenie MiHome and Nest Automation with API’s and PowerShell

22 Mar

I recently removed my OWL Intution kit in place of a Nest. A while back I also removed all my X10 kit as it was just a bit clunky, although it was fun to play with and worked most of the time, it wasn’t wife and child compatible and so I got rid. I put PIR sensors in the ceilings of all the halls, bathrooms, kitchen, utility, and garage, basically any room where you don’t generally spend a lot of time without moving. All bedrooms, the lounge and the dining room are the only rooms left with light switches, as you don’t want a light coming on when you move in the middle of the night or whilst watching a film etc.

The ceiling mounted PIR’s work perfectly all of the time, don’t need batteries, and it’s nice that they all turn themselves on when you walk by and turn off when you aren’t there.

I recently invested in a bit of Energenie MiHome kit and have a few of the plugs dotted around the house and have now replaced the remaining light switches with MiHome units.

The MiHome app isn’t bad, but one annoyance is that all timers need an on and an off time, and I really wanted to turn stuff of a scheduled time only. It does however have an API that is very useful.

The Nest app is decent, and I very rarely have to touch it now, it just works, which is the way automation should be IMO.

I don’t yet have a Google Home, or Alexa or HomePod etc. I may get one, but my main goal is for things just to work without having to actually control them at all, either with my voice or with buttons. Plus, with kids in the house, I can see anything voice controlled getting abused rather quickly. Siri already takes quite a beating from my 4-year-old daughter when it can’t answer basic questions about princesses or ponies.

I have played with IFTTT too, which is ok and offers a lot, but is limited to one IF statement and one THAT statement and so that didn’t really work for me.

So, what did I want to achieve? Well, saving energy, kinda, although if you do the maths, I probably could leave everything on 24/7 and it would cost less than the kit I’ve bought so that’s not a valid excuse. The fact that I could program and play with it, of course this is the real reason. To deter potential burglars, perhaps, a useful feature. It also had to be dead simple to use, so that anyone could use it without instruction.

I knew by using the API’s I could achieve almost everything I wanted in one place, so I quickly removed all of the timers from MiHome and set about writing my PowerShell engine. My goal was to make it as simple and configurable as possible, of course by writing it myself I could get it to do pretty much anything I wanted, and if it doesn’t work, well, I’ve only got myself to blame.

I ultimately have one PowerShell script that runs each day at 1am. This script removes all the existing scheduled tasks that I have set and removes the PowerShell files that these tasks execute and creates all the brand-new ones for the day ahead. Any changes I just make from this one master script and it goes off and does everything else, so no chance of forgetting to tick a box somewhere or change a setting here and there, kinda my attempt at DSC for home automation.

The current list of tasks that I have configured are:

  • Every 60 mins, if outside temperature above 16c and heating set to heat turn heating off, else if under 16c and heating set to off, set heating to heat.
  • At 22:00, mon-sun, if heating set to home and if outside temp < 12 turn electric blanket on
  • At 07:00, mon-sun, turn electric blanket off
  • At 07:00 mon-fri, if heating set to home, turn master bedroom lights on, unless it’s a bank holiday
  • At 08:00 sat-sun, if heating set to home, turn master bedroom lights on (Like the kids would let me sleep in ‘till 8 anyway!)
  • At sunset, mon-sun, if set to away, turn lounge light on
  • At 23:00 sun-thu, turn lounge socket off, lounge TV off, lounge light off. 23:59 fri-sat
  • Every 5 mins, if heating set to away, turn lounge tv off, lounge socket off
  • 10:00 mon-sun, turn master bedroom lights off, kids bedroom lights off

Obviously for these to work I needed some more information from external sources.

To find out if it’s a bank holiday I just pull the JSON file form the gov.uk site and cross reference it against todays date.

#find out if today is a bank holiday
$result = Invoke-WebRequest "https://www.gov.uk/bank-holidays.json" -Method Get -UseBasicParsing | convertfrom-json
$today = Get-Date -format "yyyy-MMM-dd"
$bankholidaytoday = $false
foreach($event in $result.'england-and-wales'.events)
{
    if($event.date -eq $today){
        $bankholidaytoday = $true
    }
} 

I then talk to OpenWeatherMap to get the outside temperature and sunset and sunrise times.

#Get Weather information and sun rise/sunset
$weatherapikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$weatherlocation = "Location,UK"
$result = Invoke-WebRequest "https://api.openweathermap.org/data/2.5/weather?q=$weatherlocation&APPID=$weatherapikey&units=metric" -method Get -UseBasicParsing | convertfrom-json
$outsidetemp = $result.main.temp
$origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
$sunrise = $origin.AddSeconds($result.sys.sunrise)
$sunset = $origin.AddSeconds($result.sys.sunset) 

I also setup a Nest Developer account to get access to the API there and setup API access to my Energenie MiHome kit.

I needed to know the day of the week as an INT which was easy:

#day of week as an int, mon=1 etc...
$dayofweek = [int] (get-date).DayOfWeek 

To get a list of all my Energenie MiHome device ID’s I create some variables on the fly based on their name. So if I do change a device over, as long as its name remains the same it will still work and save me having to reconfigure the device ID’s in the script:

#set mihome device ID variables
$user= "[email protected]"
$pass = "password"
$pair = "${user}:${pass}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ Authorization = $basicAuthValue }
$emptyJSON = @{}  | ConvertTo-Json
$subdevicesuri = "https://mihome4u.co.uk/api/v1/subdevices/list"
$result = Invoke-WebRequest -Headers $headers -uri $subdevicesuri -Method put -Body $emptyJSON -ContentType "application/json" -UseBasicParsing | ConvertFrom-Json

$result.data | select-object id, label | foreach-object {
    $varname = "var_" + $_.label -replace " ", "_"
    New-Variable -Name $varname -value $_.id
}

get-variable | where {$_.Name -like 'var_*'} 

I then have a stack of variables listed which I use throughout the rest of the script. I also check that the directories exists and log file is there and if not, create them too.

#variables
$rootdir = "c:\home_automation\"
$scriptdir = $rootdir + "scripts\"
$logdir = $rootdir + "logs\"
$logfile = $logdir + "events.log"
$staskprefix = "home_automation_"
$nestaccesstoken = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
$weatherapikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$thermostatid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$structureid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"


#check files and folder we need exist, and if not create them
if(!(Test-Path $rootdir)){
    new-item -itemtype directory -path $rootdir
}

if(!(Test-Path $scriptdir)){
    new-item -itemtype directory -path $scriptdir
}

if(!(Test-Path $logdir)){
    new-item -itemtype directory -path $logdir
}

if(!(Test-Path $logfile)){
    new-item -itemtype file -path $logfile
} 

I then remove any previous PowerShell files and scheduled tasks that I have created

#clear existing files
Get-ChildItem -Path $scriptdir -Include *.* -File -Recurse | foreach { $_.Delete()}

#clear existing scheduled tasks
Unregister-ScheduledTask -TaskName "$staskprefix*" -Confirm:$false 

With all that in place, all that’s left is to create all the new scheduled tasks and PowerShell files

$description = "At 22:00, mon-sun, if heating set to home and if outsidetemp < 12 turn electric blanket on"
$id = 2
$scriptpath = $scriptdir + $staskprefix + $id + ".ps1"
$script = '
Add-content '+$logfile+' -value "$(Get-Date): Running Job ID '+$id+', '+$description+'"
$result = Invoke-WebRequest "https://api.openweathermap.org/data/2.5/weather?q='+$weatherlocation+'&APPID='+$weatherapikey+'&units=metric" -method Get -UseBasicParsing | convertfrom-json
$outsidetemp = $result.main.temp

$homeawayuri = "https://developer-api.nest.com/structures/'+$structureid+'/away?auth='+$nestaccesstoken+'"
$homeaway = Invoke-WebRequest $homeawayuri -Method Get -UseBasicParsing
$homeaway = ($homeaway -replace """","")

if($outsidetemp -lt 12 -and $homeaway -eq "home"){
    Add-content '+$logfile+' -value "$(Get-Date): Turning electric blanket ON"
    #turn on electric blanket
    $user= "[email protected]"
    $pass = "password"
    $pair = "${user}:${pass}"
    $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
    $base64 = [System.Convert]::ToBase64String($bytes)
    $basicAuthValue = "Basic $base64"
    $headers = @{ Authorization = $basicAuthValue }
    $JSON = ''{"id":'+$var_electric_blanket+'}''
    $uri = "https://mihome4u.co.uk/api/v1/subdevices/power_on"
    $result = Invoke-WebRequest -Headers $headers -uri $uri -Method put -body $JSON -ContentType "application/json" -UseBasicParsing | ConvertFrom-Json
}
'
$script | Out-File $scriptpath
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-ExecutionPolicy Bypass  $scriptpath -RunType $true -path $scriptdir"
$trigger =  New-ScheduledTaskTrigger -Once -At 22:00
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName ($staskprefix+$id) -Description "Automated Task" -User "NT Authority\SYSTEM" -RunLevel Highest


$description = "At 07:00 mon-fri, if heating set to home, turn master bedroom lights on, unless it's a bank holiday"
if($dayofweek -in 1..5 -and $bankholidaytoday -eq $false){
    $id = 4
    $scriptpath = $scriptdir + $staskprefix + $id + ".ps1"
    $script = '
    Add-content '+$logfile+' -value "$(Get-Date): Running Job ID '+$id+', '+$description+'"
    $homeawayuri = "https://developer-api.nest.com/structures/'+$structureid+'/away?auth='+$nestaccesstoken+'"
    $homeaway = Invoke-WebRequest $homeawayuri -Method Get -UseBasicParsing
    $homeaway = ($homeaway -replace """","")

    if($homeaway -eq "home"){
        Add-content '+$logfile+' -value "$(Get-Date): Turning master bedroom light ON"
        #turn on bedroom lights
        $user= "[email protected]"
        $pass = "password"
        $pair = "${user}:${pass}"
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $basicAuthValue = "Basic $base64"
        $headers = @{ Authorization = $basicAuthValue }
        $JSON = ''{"id":'+$var_master_bedroom_light+'}''
        $uri = "https://mihome4u.co.uk/api/v1/subdevices/power_on"
        $result = Invoke-WebRequest -Headers $headers -uri $uri -Method put -body $JSON -ContentType "application/json" -UseBasicParsing | ConvertFrom-Json
    }
    '
    $script | Out-File $scriptpath
    $action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-ExecutionPolicy Bypass  $scriptpath -RunType $true -path $scriptdir"
    $trigger =  New-ScheduledTaskTrigger -Once -At 07:00
    Register-ScheduledTask -Action $action -Trigger $trigger -TaskName ($staskprefix+$id) -Description "Automated Task" -User "NT Authority\SYSTEM" -RunLevel Highest
} 

Some scripts run just once, some run at regular intervals, some have single checks, and some have quite a few checks. All events write to the log files I have created and so far all is good. Of course, only I can change this system, but that’s fine too as I want it to be as automatic as possible and for things just to work without user input.

I’m sure I’ll change this soon and add some more bits in. The code could do with a tidy, but I’m pretty happy with it as a V1.

I have an alarm that would allow me to get outputs when it’s set to home, away and disarmed which would be great to kick of other events. However the PM+RS-232 component that I need is impossible to get hold of, so if anyone reading this has the Serial port kit for a Visonic PowerMax+ then please let me know! I used to have this via the X10 output and kinda wish I’d kept some of my X10 kit now, doh!