Homemade Smart Power Strip

This post is not a hardware post, but just a simple way to use a smart plug to replace a conventional smart power strip to avoid unneeded standby power consumption. For my home entertainment system I previously used a conventional smart power strip, but it caused several crashes of my media center, a Raspberry Pi running Kodi. So I decided to replace it with a standard power strip connected to a smart plug, and let openHAB do the magic.

I decided to use a ZigBee smart plug due to low power consumption and since already having a Philips Hue bridge within range. I went with an Innr smart plug, since the Philips Hue smart plug hadn’t been introduced at the time.

As trigger for turning power on or off, I went with my Harmony Hub. This way I can also turn off the network switch, since the Hub is Wi-Fi connected. When any activity is started, requiring any of the powered off devices, I’ll simply turn on the smart plug. When powering off, I need Kodi to shutdown and wait for this. So here are the rules I ended up with:

rule "TV turn on socket"
when
    // This is logically correct, but too slow because it's only fired when
    // activity has completed. It's a fallback solution because of a bug in
    // the binding, see below.
    Item HarmonyHub_CurrentActivity changed from "PowerOff" or
    // These channel triggers are fast, but logic is messy because logic
    // cannot be reversed. The correct solution would be to trigger on an item
    // bound to "Activity Starting Trigger" and use same logic as above:
    // Item HarmonyHub_ActivityStartingTrigger changed from "PowerOff"
    Channel "harmonyhub:hub:HarmonyHub:activityStarting" triggered Watch_tv or
    Channel "harmonyhub:hub:HarmonyHub:activityStarting" triggered Listen_to_music or
    Channel "harmonyhub:hub:HarmonyHub:activityStarting" triggered Watch_a_movie or
    Channel "harmonyhub:hub:HarmonyHub:activityStarting" triggered Kodi or
    Channel "harmonyhub:hub:HarmonyHub:activityStarting" triggered Chromecast
then
    Tv_Socket_Switch.sendCommand(ON)
end

rule "TV turn off socket"
when
    Item HarmonyHub_CurrentActivity changed to "PowerOff"
then
    var num_of_attempts = 20
    var delay_between_attempts = 5000
    var attempt = 0

    var kodi_initial_online_state = NetworkDeviceKodi_Online.state

    // Waiting for Kodi (might be booting up)
    while (NetworkDeviceKodi_Online.state == OFF && attempt < num_of_attempts)
    {
        attempt = attempt + 1
        Thread::sleep(delay_between_attempts)
    }

    if (NetworkDeviceKodi_Online.state == OFF)
    {
        sendPushMessage.apply("Tv turned off", "Kodi is offline. Something wrong?")
    } 
    else if (kodi_initial_online_state == OFF)
    {
        // Initial state offline, now online - waiting for services to start"
        Thread::sleep(30000)
    }

    if (HarmonyHub_CurrentActivity.state == "PowerOff")
    {
        // Attempting to shut down Kodi
        sendHttpPostRequest('http://pi3:80/jsonrpc', 'application/json', '{"jsonrpc":"2.0","method":"System.Shutdown","id":1}')
        Thread::sleep(30000)

        // Waited 30 seconds, checking if activity didn't start again
        if (HarmonyHub_CurrentActivity.state == "PowerOff")
        {
            Tv_Socket_Switch.sendCommand(OFF)
        }
        else
        {
            sendPushMessage.apply("Tv turned off", "Tv turned on again after Kodi was shut down - now offline. Trying to restart.")
            Tv_Socket_Switch.sendCommand(OFF)
            Thread::sleep(5000)
            Tv_Socket_Switch.sendCommand(ON)
        }
    }
    else
    {
        // Shutdown/power off aborted, new activity started.
    }
end



I might look over-complicated, but I wanted a defensive approach to make sure everything is done to not trigger data corruption when power is taken from the Kodi Pi without proper shutdown. For example, if the Pi is still booting when TV is turned off, it won’t be able to accept a shutdown command yet, so I’m waiting for it to complete booting. Also, in case the TV is turned on again while the Pi is shutting down, I try to boot it again by turning it off and on again.