zavracky
Published © GPL3+

Shutdown on Power Blackout

When you lose AC power, your backup system takes over. This project shuts down your vulnerable equipment before the UPS dies.

IntermediateFull instructions provided2,795
Shutdown on Power Blackout

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Belkin Wemo Smart Plug
×1
UL Certified USB Wall Charger Power Supply 5v 1A (1000mA) Universal Portable Travel Power Adapter Plug
Any charger that puts out 5 Volts will do.
×1
DIKAVS Breadboard-Friendly 2.1mm PCB Mounting Female DC Power Barrel Jack (Pack of 10)
These connectors connect to the three required I/Os; the 5vdc sense input (used to determine if AC power has failed), the 5VDC 4Amp input from the supply powered by the UPS, and the relay switched 5VDC output to the RPi.
×3
USB to 2.1 mm cable
This cable connects a 5 Volt wall USB charger to the 2.1mm input on the enclosure.
×1
NRF24 Radio
×1
Signal Relay, 5 VDC
Signal Relay, 5 VDC
I bought relays from amazon: https://www.amazon.com/gp/product/B07SP9KY35/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Power Monitor Enclosure

Power Monitor Cover

File missing, please reupload.

Schematics

Power Monitor Circuit Board

This board uses an external usb power supply to monitor the voltage on an outlet that is not backed up. If the power is lost, the Arduino will initiate a shut down process to first shut down any designated PCs on the network, if desired, it will shut down any items connected to IOT switches, and finally, will shut down the RPi.

Even though I used the extra wide leads on the PCB, the current carrying capability of the board was not sufficient to power the Raspberry. Therefore, I soldered 18 awg wires to the bottom of the board connecting the positive terminals of the USB connectors directly to the relay. Then I connected the grounds of the USB connectors together.

Code

Power Monitor Arduino Code v4.1

C/C++
The current timing of this code is set up for testing. Starting at line 81 are a list of defined times that should be adjusted to match individual system requirements.
/**
 * Home Power Detection Monitor
 * The purpose of this device is to indicate when there is a power loss.  It uses a circuit originally designed as a backup system,
 * which did not perform adequately.  Now, it takes input from a UPS and/or 5 Volt power supply.  The 5V supply is connected to the 
 * home's AC power source.  The circuit senses the voltage from the 5 volt supply as a digital signal (connected to D3).  
 * At AC power failure: the UPS provides power to the circuit.  The circuit senses the power loss and sends a signal to Homeassistant.  
 * When AC power is available, the circuit is once again powered by it.
 * 
 * The Home Assistant timer input sets the time in minutes until Home Assistant initiates it's own shut down.  After Home Assistant has
 * shut down, the Arduino turns off the relay that provides power to the Raspberry.  One of two possibilities will occur subsequently.
 *    1) if the APS continues to power the arduino as the utility power returns, the Arduino senses the return of power and applies power to 
 *    the Raspberry, rebooting the system.
 *    2) if the APS power dies before the utility power returns, then when the utility power does return, the Arduino delays for a preset
 *    amount of time to insure power stability, then applies power to the Raspberry, which reboots.
 * 
 * Upon power failure, HomeAssistant sends an alert to email.  Home Assistant will also shut down the PCs on the network.  Once 
 * everything is shut down, Home Assistant will send a command to the Arduino, to turn off the power to the Raspberry (which is running 
 * home assistant).  The execution of the command is delayed for RPI_SHUTDOWN_TIME (see below).  During this time, Home Assistant will 
 * shut down itself and HASSIO (the operating system).  Some time later, the Arduino completes its time out (RPI_SHUTDOWN_TIME) and 
 * turns off the power to the Raspberry using the Relay.  
 * 
 * Upon the return of Power, if the APC UPS ran out of power before the AC power returned, then the Arduino will be rebooting.  As part 
 * of this process, it will reapply the power to the Raspberry, which will cause it to reboot.  If on the other hand, the APC UPS is 
 * still providing power upon return of AC power, the Arduino will still be running.  When it detects that the external power has returned, 
 * it will wait for (RPI_SHUTDOWN_TIME) before applying power to the Raspberry.  This is to avoid attempting to restart the Raspberry 
 * before power has stablized.  Once Home Assistant is back on line, it will turn on the Wemo switches causing all the pcs on the network 
 * to reboot as well.
 * 
 * By removing the PCs from the APC UPS, the power requirement for the UPS will be drastically reduced, extending its up time.  Hopefully, 
 * this would be longer than any power outage.  For Sharon, there are two pcs and two UPSs; one for the network router and associated 
 * equipment, and one for the Raspberry and office pc.  
 * 
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 * REVISION HISTORY
 * Version 1.0 - Henrik Ekblad
 * Heavily modified by Paul Zavracky, October 2020
 * See version number below
  */
#define VERSION "4.1"
#define NAME "HOME_PWR_DETECTION_MONITOR"

// Enable debug prints to serial monitor
#define MY_DEBUG 

// Enable and select radio type attached
#define MY_RADIO_RF24
#define MY_NODE_ID 14

#include <MySensors.h>

#define RELAY_PIN 8  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define POWER_VOLTAGE_SENSE_PIN A7 // Arduino pin used to sense the external 110VAC power source.  If this goes low, we've lost power.

#define AC_POWER_STATE_CHILD_ID 2 // ID of power sense pin (takes input from a 5v supply plugged into AC power source).
#define TIMER_CHILD_ID 4 // this is used for the countdown timer.  It tell HA the number of seconds on the clock.
#define POWER_VOLTAGE_CHILD_ID 11 // leaving room for LED light readings - not really necessary
#define PC_TRANSITION_CHILD_ID 12 // this output tells HASS to shutdown the PCs
#define RPI_TRANSITION_CHILD_ID 13 // this output tells HASS to shutdown the RPi
#define WEMO_CHILD_ID 14 // this is used to tell HASS to access the Wemo switch
#define HASS_BOOT_CHILD_ID 15 // this message will let the Arduino know that HASS has booted (no longer used)
#define SYS_STATE_CHILD_ID 16 // this is text representing the state of the system

#define SHUTDOWN_DELAY .2 // time in minutes before a shutdown occurs in response to a power loss
#define REFRESH_TIME 12 // time in seconds between full data refreshes during idle periods
#define ACTIVE_UPDATE_TIME 1 // when in shutdown or startup mode, this is the update rate in seconds
#define RPI_SHUTDOWN_TIME .2 // time in minutes to allow Raspberry to shutdown before switching off power
#define PC_BOOT_TIME .2 // time in minutes to allow the PC to boot. Actual boot time includes booting the RPi
#define PC_SHUTDOWN_TIME .2 // time in minutes to allow PCs to shutdown before switching off power

#define RELAY_ON 1
#define RELAY_OFF 0

//MyMessage msgRelay(RELAY_CHILD_ID, V_TRIPPED); // set up switch for relay - we don't need this
MyMessage msgSense(AC_POWER_STATE_CHILD_ID, V_TRIPPED); // set up as binary sensor
MyMessage msgTimer(TIMER_CHILD_ID, V_LEVEL); // used to display countdown timer
MyMessage msgAO3(POWER_VOLTAGE_CHILD_ID, V_VOLTAGE); // AC Power Voltage Indicator
MyMessage msgPCTransition(PC_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down PC
MyMessage msgRPITransition(RPI_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down itself and RPi
MyMessage msgWemo(WEMO_CHILD_ID, V_TRIPPED); // this is used to tell HASS to access the Wemo switch
MyMessage msgHASS(HASS_BOOT_CHILD_ID, V_TRIPPED); // HASS is up message which shortcuts timer loop
MyMessage msgSysState(SYS_STATE_CHILD_ID, V_TEXT); // like it says, this reflects the current state of the system

bool powerState = true;
bool oldPowerState; // used in the main loop to determine if the powerState has changed
bool sendPowerState; // used to control the sending of the power state to HA
bool PCshutdownState = false; // the notification state when the HA should shutdown the RPi
bool firstLoop = true; // forces a refresh during first loop
bool haveReset = true;
bool PCTransFlag = false; // this is true when the PC is transitioning (booting or shutting down)
bool oldPCTransFlag = false; // used to limit sends to HA
bool RPITransFlag = false; // this is true during the time when the RPi is transitioning 
bool oldRPITransFlag = false; // used to limit sends to HA
bool updateFlag = false; // used to trigger active updates (the times when the system is 
bool counterFlag = false; // determines whether or not the countup timer is active
bool shutdownDelayFlag = false; // used to keep track of the shutdown delay period
bool RPiBootPrintFlag = true; // gets ready to print out RPi boot message only once

// The three flags below are used in the routines that confirm HA has received an automation triggering event
bool HassBootedFlag = false; // used to tell when HASS has booted (check by turning on the Wemo switch, thus booting the PC)
bool PCTransFlagRecieved = false; // used to insure HASS will run automation to shutdown Windows10
bool RPiTransFlagRecieved = false; // used to insure HASS will run automation to shutdown RPi

//
uint16_t powerVoltageLevel = 0; // reading of voltage at system power input (reflects AC state)
uint16_t oldpowerVoltageLevel = 0; 
unsigned long lastrefreshTime = millis();// time in milliseconds of last update: update every REFRESH_TIME
unsigned long lastCountDownTime = millis(); // used for inloop timers
int countTime = 0;
int oldCountTime = 0; // used in the Refresh routine to limit repeat sends to HA
float voltage = 0.00; // setup variable for analog output to HASS
float oldVoltage = 0.00; // used by Refresh to limit repeat sending of data to HA
enum {powerLossed, powerRestored, systemOff, systemOn} sysState;
int oldSysState; // used to limit sends to HA

void before() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  Serial.print(NAME); Serial.print("    Version "); Serial.println(VERSION);
  Serial.println("booting RPi for the first time");
  digitalWrite(RELAY_PIN, RELAY_ON); // turn on relay when Arduino does a cold start.  Boots Raspberry
}

// boot stalls here while MySensors waits for the RPi to boot

void setup()  
{  
}

void presentation()  {
  // Send the sketch version information to the gateway and Controller
  sendSketchInfo(NAME, VERSION);

  // Register all sensors to gw (they will be created as child devices)
  //present(RELAY_CHILD_ID,S_LIGHT); // controls power to Raspberry
  present(AC_POWER_STATE_CHILD_ID, S_DOOR); // use S_DOOR to get binary sensor output
  present(TIMER_CHILD_ID, S_LIGHT_LEVEL, "Current Count"); // current timer count
  present(POWER_VOLTAGE_CHILD_ID, S_MULTIMETER, "Input Voltage Level");
  present(PC_TRANSITION_CHILD_ID, S_DOOR);
  present(RPI_TRANSITION_CHILD_ID, S_DOOR);
  present(WEMO_CHILD_ID, S_DOOR);
  present(HASS_BOOT_CHILD_ID, S_DOOR);
  present(SYS_STATE_CHILD_ID, S_INFO);
}

void loop() 
{
  getVoltage(); // keep an eye on the voltage, afterall, this is the purpose.  This function sets the powerState variable!
  if (firstLoop) { // setting oldPowerState this way will force execution of if statement below  
    // at this point, the RPi has booted to HASS
    send(msgHASS.set(false)); // this is needed to make sure that HASS registers this binary sensor
    oldPowerState = powerState; // will bypass next if statement
    sysState = powerRestored;  // this will boot system.  The only way to get here is on reboot of the Arduino
    counterFlag = false;
    refresh(); // make sure HA reflects system off condition
  }
  if (oldPowerState != powerState) { // state change
    //let's first check to see if the system is booting or shutting down.  If so, wait (no action)
            // let's determine the new system state
            // this simple code takes care of a power change during the time the system is booting or shutting down by
            // allowing the current state (booting or shutting down) to complete, and then changing the state to match
            // with the power condition.
    if (powerState && sysState == systemOff) { // power is on but system is off.  Turn it on!
      sysState = powerRestored;
      oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down
      refresh();  // make sure HA reflects system off condition
      delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition
    }
    else if (!powerState && sysState == systemOn){ // power is off but system is on.  Turn it off!
      sysState = powerLossed;
      oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down
      refresh();  // make sure HA reflects system off condition
      delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition
    }
  }
// the above happens when the powerState changes, the below continues countdowns and updates counter while counterFlag is true.

  switch (sysState) {
    case powerLossed: // we are currently starting or doing a countdown to turn off the PC and the Raspbery
      if(!PCTransFlag && !RPITransFlag && !shutdownDelayFlag){ // first time through loop this will be true
        shutdownDelayFlag = true;
        Serial.println("Shutdown delay initiated"); // start PC shutdown process
        lastCountDownTime = millis();
        updateFlag = true; // causes update every ACTIVE_UPDATE_TIME
        counterFlag = true; // get the countup clock running
      }
      if (shutdownDelayFlag) {
        if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over
        if (millis() - lastCountDownTime > SHUTDOWN_DELAY * 60000) {// time has run out, must start shutdown delay
          shutdownDelayFlag = false;
          PCTransFlag = true;
          lastCountDownTime = millis();
          Serial.println("PC shutdown initiated"); // start PC shutdown process
        }
        else if (powerState && sysState == powerLossed) { // we are shutting down the system, but the power just went back on
          sysState = systemOn; // the system state is still on, it hasn't been turned off yet. Reseting sysState will exit loop
          oldPowerState = powerState; // get set for next state change
          shutdownDelayFlag = false;
          updateFlag = false;
          counterFlag = false;
          refresh(); // reflect new state immediately
          Serial.println("Shutdown process terminated"); // abort shutdown process
          break; // exit switch/case
        }
      }      
      if (PCTransFlag) { // shutdown Windows10 and turn off the PC
        if (!PCTransFlagRecieved) { // HA did not catch the transition of PC_TRANSITION_CHILD_ID and thus did not run the 
                                    // automation to shut down Windows10.  So, we have to try to trigger it again
                                    // If this never goes, the below shutdown countdown will just pull the plug on the PC
          oldPCTransFlag = false; PCTransFlag = true; // this will force refresh() to send(msgPCTransition.set(PCTransFlag))
        }
        if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over
        if (millis() - lastCountDownTime > PC_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC
                                                              // and start Raspberry shutdown
          Serial.println("PC shutdown complete, power disconnected");
          PCTransFlag = false;
          RPITransFlag = true;
          lastCountDownTime = millis();
          send(msgWemo.set(false)); // tells HA to turn on WEMO switch to PC
          Serial.println("Shutting down HA and RPi");
        }
      }
      if(RPITransFlag) { // shut down Hassio and turn off the RPi
        if (!RPiTransFlagRecieved) { // HA did not catch the transition of RPI_TRANSITION_CHILD_ID and thus did not run the 
                                    // automation to shut down Hassio.  So, we have to try to trigger it again
                                    // If this never goes, the below shutdown countdown will just pull the plug on the RPi - awful!
          oldRPITransFlag = false; RPITransFlag = true; // this will force refresh() to send(msgRPITransition.set(RPiTransFlag))
        }
        if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over
        if (millis() - lastCountDownTime > RPI_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC
          digitalWrite(RELAY_PIN, RELAY_OFF); // turn off power to Raspberry, it should already be shutdown
          Serial.println("RPi turned off");
          RPITransFlag = false;
          sysState = systemOff;
          updateFlag = false; // stop 1 second updates
          counterFlag = false;
          HassBootedFlag = false; // reset this and below for when power returns
          PCTransFlagRecieved = false;
          RPiTransFlagRecieved = false;
          Serial.println("System Shutdown");
        }
      }
      break;
      
    case powerRestored:
      if(!RPITransFlag && !PCTransFlag) { // first time through loop this will be true
        digitalWrite(RELAY_PIN, RELAY_ON); // turn on power to Raspberry, it should reboot
                                       // no point in notifying HA at this point 
        HassBootedFlag = false; // set this false for periodic checks on each loop to see if HA is live  
        RPITransFlag = true;
        PCTransFlag = false;  // make sure this is off during RPi boot
        Serial.println("power just went on.  RPi boot initiated");
        lastCountDownTime = millis();
        counterFlag = true; // start countup timer
      }
      if(RPITransFlag) {
        if(firstLoop) { // the Raspberry has already booted, no need to wait here!
          Serial.println("RPi already on!");
          RPITransFlag = false; // RPI has booted already
          PCTransFlag = true; // time to move on to booting the PC, move to next if statement
          lastCountDownTime = millis();
          refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME
          send(msgWemo.set(true)); // tells HA to turn on WEMO switch to PC
          firstLoop = false;          
        }
        else {
          if(RPiBootPrintFlag) { // will print only once based on this flag
            Serial.println("Must delay while RPi boots.");
            RPiBootPrintFlag = false;
          }
          if (HassBootedFlag) {// raspberry has booted, must boot PC, Wemo switch is turned on during refreshes
                               // which double as a check to see if HA is live
            Serial.println("RPi has booted!");
            RPITransFlag = false; // RPI has booted already
            PCTransFlag = true;
            RPiBootPrintFlag = true; // reset this flag for next cycle
            lastCountDownTime = millis();
            refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME
            delay(5); send(msgWemo.set(true)); // tells HA to turn on WEMO switch to PC.  This should already have been done, so this is an insurance policy
          }
        }
      }
      if (PCTransFlag) {
        if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over
        if (millis() - lastCountDownTime > PC_BOOT_TIME * 60000) {// time has run out, must disconnect power from the PC
                                                              // and start Raspberry shutdown
          Serial.println("PCs should be running");
          PCTransFlag = false;
          sysState = systemOn; // this causes an exit of this case and moves to systemOn, an idle state.
          updateFlag = false; // stop 1 second updates
          counterFlag = false; // turn off countup timer
          countTime = 0;
          refresh();
          Serial.println("System Booted");
        }
      }
      break;
    case systemOff:
      break;
    case systemOn:
      break;
  }
  //Serial.print("millis() - lastrefreshTime = ");Serial.println(millis() - lastrefreshTime);
  //Serial.print("update time = ");Serial.println(updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME);
  if (millis() < lastrefreshTime) {lastrefreshTime = 0; } // clock rolled over
  if ((millis() - lastrefreshTime) > ((updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME) * 1000)) {
    lastrefreshTime = millis();
    if (counterFlag) {
      countTime = int((millis()-lastCountDownTime)/1000);
    }
    else {
      countTime = 0; // if we are not counting a state change process, make sure the counter reads 0
    }
    if (!HassBootedFlag && RPITransFlag) { // if true, let's check to see if HA is responding
      send(msgWemo.set(true), true); // this not only turns on the Wemo, but asks for an ACK from HA
    }
    refresh();
    //Serial.print("SystState = ");Serial.println(sysState);
    //Serial.print("countTime = "); Serial.println(countTime);
  }
}

void receive(const MyMessage &message) {
  byte relayPinIndex;
  
  if (message.isAck()) {
    Serial.println("This is an ack from gateway");
    Serial.print("sensor = ");Serial.println(message.sensor);
    if(message.sensor == WEMO_CHILD_ID) { // using the Wemo sensor(14) to both check for HA and turn on PC
      HassBootedFlag = true; // The RPi and HA are up and running!
      Serial.print("ACK for Wemo, it's "); Serial.println(message.getBool() ? "On" : "Off" );
    }
    if(message.sensor == PC_TRANSITION_CHILD_ID) { // this message says that HA knows to run the PC shutdown or boot automations
      PCTransFlagRecieved = true; // We have an ACK from gateway on the PCTransFlag message.
      Serial.print("PCTransFlag = "); Serial.println(message.getBool() ? "true" : "false" );      
    }
    if(message.sensor == RPI_TRANSITION_CHILD_ID) { // this message says that HA knows to run the RPi shutdown automation
      RPiTransFlagRecieved = true; // We have an ACK from gateway on the RPiTransFlag message.
      Serial.print("RPiTransFlag = "); Serial.println(message.getBool() ? "true" : "false" );      
    }
  }
}

void refresh() { 
  getVoltage();
  // below, only update on transitions
  if(PCTransFlag != oldPCTransFlag){send(msgPCTransition.set(PCTransFlag),true); oldPCTransFlag = PCTransFlag;} // use to tell HA to shut down PC
  if(RPITransFlag != oldRPITransFlag){send(msgRPITransition.set(RPITransFlag),true); oldRPITransFlag = RPITransFlag;} // use to tell HA to shut down itself and RPi
  // below, if updateFlag is true, update only changes, if false, update all
  if(powerState != sendPowerState || !updateFlag) {send(msgSense.set(powerState)); sendPowerState = powerState;}  
  if(countTime != oldCountTime || !updateFlag) {send(msgTimer.set(countTime)); oldCountTime = countTime;} // current countdown time in seconds
  if(voltage != oldVoltage || !updateFlag) {send(msgAO3.set(voltage,2));  oldVoltage = voltage;}
  if(sysState != oldSysState || !updateFlag) {
    oldSysState = sysState;
    switch (sysState) {
      case powerLossed:
        send(msgSysState.set("powerLossed"));
        break;
      case powerRestored:
        send(msgSysState.set("powerRestored"));
        break;
      case systemOff:
        send(msgSysState.set("systemOff"));
        break;
      case systemOn:
        send(msgSysState.set("systemOn"));
        break;  
    }
  }
}

void getVoltage() {
  powerVoltageLevel = analogRead(POWER_VOLTAGE_SENSE_PIN); 
  //Serial.print("powerVoltageLevel = "); Serial.println(powerVoltageLevel);
  voltage = 5.00 * powerVoltageLevel / 998; // Arduino input does not pull up but does have a series resistor between input pin and Arduino A7
  if(powerVoltageLevel > 100) { powerState = true;} else {powerState = false;}
  //Serial.print("voltage = "); Serial.println(voltage);
}

Power Monitor Test Yaml

YAML
By removing some lines from the Home Assistant "Power Monitor" automation, tests of the system can be conducted without switching power off to the PC or Raspberry. This allows you to safely test to make sure that everything is working as it should. In my testing, I also modified the times in the Arduino code to shorten the test period. So:

#define RPI_BOOT_TIME .2 // time in minutes until Raspberry has booted
#define RPI_SHUTDOWN_TIME .2 // time in minutes to allow Raspberry to shutdown before switching off power
#define PC_BOOT_TIME .2 // time in minutes to allow the PC to boot. Actual boot time includes RPI_BOOT_TIME
#define PC_SHUTDOWN_TIME .2 // time in minutes to allow PCs to shutdown before switching off
################################################################################
########### Power Monitor Automations ##########################################

  - alias: "Power Loss Alert"
    trigger:
    - platform: state
      entity_id: binary_sensor.home_pwr_detection_monito_14_2
      from: 'on'
      to: 'off'
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Power Alert"
        
  - alias: "Power Returned Alert"
    trigger:
    - platform: state
      entity_id: binary_sensor.home_pwr_detection_monito_14_2
      to: 'on'
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Returned"
        title: "Power Alert"
        
  - alias: "Shut down PC"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_12
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerLossed"         
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "PC Shutdown Initiated"
#    - service: hassio.addon_stdin
#      data:
#        addon: core_rpc_shutdown
#        input: Sharon_HP

  - alias: "Boot PC"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_12
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "binary_sensor.home_pwr_detection_monito_14_16"
        state: "powerRestored"         
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "PC Boot Up Initiated"

  - alias: "Shut down RPi"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_13
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerLossed"        
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Raspbery Shutdown Initiated"
 #   - service: hassio.host_shutdown

  - alias: "Boot RPi"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_13
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerRestored"        
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Raspbery Boot Up Initiated"

  - alias: "PC Wemo Power Off"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_14
        from: "on"
        to: "off"
    action:
    - service: notify.gmail
      data:
        message: "Sharon PC Power Turned off"
        title: "PC Power"
    - service: homeassistant.turn_off
      entity_id: switch.small_wemo_1 # this is actually the HP power switch

  - alias: "PC Wemo Power On"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_14
        from: "off"
        to: "on"
    action:
    - service: notify.gmail
      data:
        message: "Sharon PC Power Turned on"
        title: "PC Power"
    - service: homeassistant.turn_on
      entity_id: switch.small_wemo_1 # this is actually the HP power switch

  - alias: "RPi Boot Message"
    trigger:
      - platform: homeassistant
        event: start
    action:
      service: python_script.set_state
      data_template:
        entity_id: binary_sensor.home_pwr_detection_monito_14_15
        state: 'on'      

Power Monitor HA Automations

YAML
These Home Assistant automations send messages through gmail to alert the home owner of a loss of power and the regaining of power. In both cases, a binary switch set up by the Arduino code triggers a notification. These automations do not take any actions. Further actions are initiated by other HA automations or by the Arduino as detail in the comments at the top of the Arduino code.

Several lines are commented out in this yaml file. These lines effectuate the shutdown of the PC and RPi. When commented out, neither the PC or RPi will shut down. The code is used this way during testing. Note: when testing the Wemo plug and the Power port are not connected to the PC and RPi. See the testing section of the 'Story.'
  - alias: "Power Loss Alert"
    trigger:
    - platform: state
      entity_id: binary_sensor.home_pwr_detection_monito_14_2
      from: 'on'
      to: 'off'
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Power Alert"
        
  - alias: "Power Returned Alert"
    trigger:
    - platform: state
      entity_id: binary_sensor.home_pwr_detection_monito_14_2
      to: 'on'
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Returned"
        title: "Power Alert"
        
  - alias: "Shut down PC"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_12
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerLossed"         
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "PC Shutdown Initiated"
    - service: hassio.addon_stdin
      data:
        addon: core_rpc_shutdown
        input: Sharon_HP

  - alias: "Boot PC"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_12
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "binary_sensor.home_pwr_detection_monito_14_16"
        state: "powerRestored"         
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "PC Boot Up Initiated"

  - alias: "Shut down RPi"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_13
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerLossed"        
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Raspbery Shutdown Initiated"
    - service: hassio.host_shutdown

  - alias: "Boot RPi"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_13
        from: 'off'
        to: 'on'
    condition: 
      - condition: state
        entity_id: "sensor.home_pwr_detection_monito_14_16"
        state: "powerRestored"        
    action:
    - service: notify.gmail
      data:
        message: "Sharon Power Outage"
        title: "Raspbery Boot Up Initiated"

  - alias: "PC Wemo Power Off"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_14
        from: "on"
        to: "off"
    action:    
    - service: homeassistant.turn_off
      entity_id: switch.sharon_rpi # this is actually the HP power switch

  - alias: "PC Wemo Power On"
    trigger:
      - platform: state
        entity_id: binary_sensor.home_pwr_detection_monito_14_14
        from: "off"
        to: "on"
    action:    
    - service: homeassistant.turn_on
      entity_id: switch.sharon_rpi # this is actually the HP power switch

  - alias: "RPi Boot Message"
    trigger:
      - platform: homeassistant
        event: start
    action:
      service: python_script.set_state
      data_template:
        entity_id: binary_sensor.home_pwr_detection_monito_14_15
        state: 'on'

Lovelace Interface

YAML
This yaml file creates a page for the Power Monitor in Home Assistant.
  - title: Power Monitor
    cards:
      - type: entities
        title: Power Monitor
        show_header_toggle: false
        entities:
        - entity: binary_sensor.home_pwr_detection_monito_14_2
          name: Power Condition (booted/shutdown)
        - entity: sensor.home_pwr_detection_monito_14_16
          name: System State (booted/shutdown)
        - entity: sensor.home_pwr_detection_monito_14_4
          name: Countup Timer
          icon: mdi:clock
        - entity: binary_sensor.home_pwr_detection_monito_14_12
          name: PC transitioning (on/off))
        - entity: binary_sensor.home_pwr_detection_monito_14_13
          name: RPi transitioning (on/off)
        - entity: binary_sensor.home_pwr_detection_monito_14_14
          name: PC Power Switch (Wemo)

Set_State.py

C/C++
This a great bit of code written by Rod Payne which is used in this project to change the state of binary sensors in Home Assistant.
# by Rod Payne 
inputEntity = data.get('entity_id')
if inputEntity is None:
    logger.warning("===== entity_id is required if you want to set something.")
elif hass.states.get(inputEntity) is None:
    logger.warning("===== unknown entity_id: %s", inputEntity)
else:
    inputStateObject = hass.states.get(inputEntity)
    inputState = inputStateObject.state
    inputAttributesObject = inputStateObject.attributes.copy()

    for item in data:
        newAttribute = data.get(item)
        logger.debug("===== item = {0}; value = {1}".format(item,newAttribute))
        if item == 'entity_id':
            continue            # already handled
        elif item == 'state':
            inputState = newAttribute
        else:
            inputAttributesObject[item] = newAttribute

    hass.states.set(inputEntity, inputState, inputAttributesObject)

Credits

zavracky
1 project • 2 followers
Contact

Comments

Please log in or sign up to comment.