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.
Components and supplies
USB to 2.1 mm cable
Belkin Wemo Smart Plug
NRF24 Radio
DIKAVS Breadboard-Friendly 2.1mm PCB Mounting Female DC Power Barrel Jack (Pack of 10)
Arduino Nano R3
Signal Relay, 5 VDC
UL Certified USB Wall Charger Power Supply 5v 1A (1000mA) Universal Portable Travel Power Adapter Plug
Tools and machines
3D Printer (generic)
Project description
Code
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.'
1 - alias: "Power Loss Alert" 2 trigger: 3 - platform: state 4 entity_id: binary_sensor.home_pwr_detection_monito_14_2 5 from: 'on' 6 to: 'off' 7 action: 8 - service: notify.gmail 9 data: 10 message: "Sharon Power Outage" 11 title: "Power Alert" 12 13 - alias: "Power Returned Alert" 14 trigger: 15 - platform: state 16 entity_id: binary_sensor.home_pwr_detection_monito_14_2 17 to: 'on' 18 action: 19 - service: notify.gmail 20 data: 21 message: "Sharon Power Returned" 22 title: "Power Alert" 23 24 - alias: "Shut down PC" 25 trigger: 26 - platform: state 27 entity_id: binary_sensor.home_pwr_detection_monito_14_12 28 from: 'off' 29 to: 'on' 30 condition: 31 - condition: state 32 entity_id: "sensor.home_pwr_detection_monito_14_16" 33 state: "powerLossed" 34 action: 35 - service: notify.gmail 36 data: 37 message: "Sharon Power Outage" 38 title: "PC Shutdown Initiated" 39 - service: hassio.addon_stdin 40 data: 41 addon: core_rpc_shutdown 42 input: Sharon_HP 43 44 - alias: "Boot PC" 45 trigger: 46 - platform: state 47 entity_id: binary_sensor.home_pwr_detection_monito_14_12 48 from: 'off' 49 to: 'on' 50 condition: 51 - condition: state 52 entity_id: "binary_sensor.home_pwr_detection_monito_14_16" 53 state: "powerRestored" 54 action: 55 - service: notify.gmail 56 data: 57 message: "Sharon Power Outage" 58 title: "PC Boot Up Initiated" 59 60 - alias: "Shut down RPi" 61 trigger: 62 - platform: state 63 entity_id: binary_sensor.home_pwr_detection_monito_14_13 64 from: 'off' 65 to: 'on' 66 condition: 67 - condition: state 68 entity_id: "sensor.home_pwr_detection_monito_14_16" 69 state: "powerLossed" 70 action: 71 - service: notify.gmail 72 data: 73 message: "Sharon Power Outage" 74 title: "Raspbery Shutdown Initiated" 75 - service: hassio.host_shutdown 76 77 - alias: "Boot RPi" 78 trigger: 79 - platform: state 80 entity_id: binary_sensor.home_pwr_detection_monito_14_13 81 from: 'off' 82 to: 'on' 83 condition: 84 - condition: state 85 entity_id: "sensor.home_pwr_detection_monito_14_16" 86 state: "powerRestored" 87 action: 88 - service: notify.gmail 89 data: 90 message: "Sharon Power Outage" 91 title: "Raspbery Boot Up Initiated" 92 93 - alias: "PC Wemo Power Off" 94 trigger: 95 - platform: state 96 entity_id: binary_sensor.home_pwr_detection_monito_14_14 97 from: "on" 98 to: "off" 99 action: 100 - service: homeassistant.turn_off 101 entity_id: switch.sharon_rpi # this is actually the HP power switch 102 103 - alias: "PC Wemo Power On" 104 trigger: 105 - platform: state 106 entity_id: binary_sensor.home_pwr_detection_monito_14_14 107 from: "off" 108 to: "on" 109 action: 110 - service: homeassistant.turn_on 111 entity_id: switch.sharon_rpi # this is actually the HP power switch 112 113 - alias: "RPi Boot Message" 114 trigger: 115 - platform: homeassistant 116 event: start 117 action: 118 service: python_script.set_state 119 data_template: 120 entity_id: binary_sensor.home_pwr_detection_monito_14_15 121 state: 'on'
Power Monitor Arduino Code v4.1
c_cpp
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.
1/** 2 * Home Power Detection Monitor 3 * The purpose of this device is to indicate when there is a power loss. It uses a circuit originally designed as a backup system, 4 * 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 5 * home's AC power source. The circuit senses the voltage from the 5 volt supply as a digital signal (connected to D3). 6 * At AC power failure: the UPS provides power to the circuit. The circuit senses the power loss and sends a signal to Homeassistant. 7 * When AC power is available, the circuit is once again powered by it. 8 * 9 * The Home Assistant timer input sets the time in minutes until Home Assistant initiates it's own shut down. After Home Assistant has 10 * shut down, the Arduino turns off the relay that provides power to the Raspberry. One of two possibilities will occur subsequently. 11 * 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 12 * the Raspberry, rebooting the system. 13 * 2) if the APS power dies before the utility power returns, then when the utility power does return, the Arduino delays for a preset 14 * amount of time to insure power stability, then applies power to the Raspberry, which reboots. 15 * 16 * Upon power failure, HomeAssistant sends an alert to email. Home Assistant will also shut down the PCs on the network. Once 17 * everything is shut down, Home Assistant will send a command to the Arduino, to turn off the power to the Raspberry (which is running 18 * home assistant). The execution of the command is delayed for RPI_SHUTDOWN_TIME (see below). During this time, Home Assistant will 19 * shut down itself and HASSIO (the operating system). Some time later, the Arduino completes its time out (RPI_SHUTDOWN_TIME) and 20 * turns off the power to the Raspberry using the Relay. 21 * 22 * 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 23 * 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 24 * still providing power upon return of AC power, the Arduino will still be running. When it detects that the external power has returned, 25 * it will wait for (RPI_SHUTDOWN_TIME) before applying power to the Raspberry. This is to avoid attempting to restart the Raspberry 26 * 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 27 * to reboot as well. 28 * 29 * By removing the PCs from the APC UPS, the power requirement for the UPS will be drastically reduced, extending its up time. Hopefully, 30 * this would be longer than any power outage. For Sharon, there are two pcs and two UPSs; one for the network router and associated 31 * equipment, and one for the Raspberry and office pc. 32 * 33 * The MySensors Arduino library handles the wireless radio link and protocol 34 * between your home built sensors/actuators and HA controller of choice. 35 * The sensors forms a self healing radio network with optional repeaters. Each 36 * repeater and gateway builds a routing tables in EEPROM which keeps track of the 37 * network topology allowing messages to be routed to nodes. 38 * 39 * Created by Henrik Ekblad <henrik.ekblad@mysensors.org> 40 * Copyright (C) 2013-2015 Sensnology AB 41 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 42 * 43 * Documentation: http://www.mysensors.org 44 * Support Forum: http://forum.mysensors.org 45 * 46 * This program is free software; you can redistribute it and/or 47 * modify it under the terms of the GNU General Public License 48 * version 2 as published by the Free Software Foundation. 49 * 50 ******************************* 51 * REVISION HISTORY 52 * Version 1.0 - Henrik Ekblad 53 * Heavily modified by Paul Zavracky, October 2020 54 * See version number below 55 */ 56#define VERSION "4.1" 57#define NAME "HOME_PWR_DETECTION_MONITOR" 58 59// Enable debug prints to serial monitor 60#define MY_DEBUG 61 62// Enable and select radio type attached 63#define MY_RADIO_RF24 64#define MY_NODE_ID 14 65 66#include <MySensors.h> 67 68#define RELAY_PIN 8 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) 69#define POWER_VOLTAGE_SENSE_PIN A7 // Arduino pin used to sense the external 110VAC power source. If this goes low, we've lost power. 70 71#define AC_POWER_STATE_CHILD_ID 2 // ID of power sense pin (takes input from a 5v supply plugged into AC power source). 72#define TIMER_CHILD_ID 4 // this is used for the countdown timer. It tell HA the number of seconds on the clock. 73#define POWER_VOLTAGE_CHILD_ID 11 // leaving room for LED light readings - not really necessary 74#define PC_TRANSITION_CHILD_ID 12 // this output tells HASS to shutdown the PCs 75#define RPI_TRANSITION_CHILD_ID 13 // this output tells HASS to shutdown the RPi 76#define WEMO_CHILD_ID 14 // this is used to tell HASS to access the Wemo switch 77#define HASS_BOOT_CHILD_ID 15 // this message will let the Arduino know that HASS has booted (no longer used) 78#define SYS_STATE_CHILD_ID 16 // this is text representing the state of the system 79 80#define SHUTDOWN_DELAY .2 // time in minutes before a shutdown occurs in response to a power loss 81#define REFRESH_TIME 12 // time in seconds between full data refreshes during idle periods 82#define ACTIVE_UPDATE_TIME 1 // when in shutdown or startup mode, this is the update rate in seconds 83#define RPI_SHUTDOWN_TIME .2 // time in minutes to allow Raspberry to shutdown before switching off power 84#define PC_BOOT_TIME .2 // time in minutes to allow the PC to boot. Actual boot time includes booting the RPi 85#define PC_SHUTDOWN_TIME .2 // time in minutes to allow PCs to shutdown before switching off power 86 87#define RELAY_ON 1 88#define RELAY_OFF 0 89 90//MyMessage msgRelay(RELAY_CHILD_ID, V_TRIPPED); // set up switch for relay - we don't need this 91MyMessage msgSense(AC_POWER_STATE_CHILD_ID, V_TRIPPED); // set up as binary sensor 92MyMessage msgTimer(TIMER_CHILD_ID, V_LEVEL); // used to display countdown timer 93MyMessage msgAO3(POWER_VOLTAGE_CHILD_ID, V_VOLTAGE); // AC Power Voltage Indicator 94MyMessage msgPCTransition(PC_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down PC 95MyMessage msgRPITransition(RPI_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down itself and RPi 96MyMessage msgWemo(WEMO_CHILD_ID, V_TRIPPED); // this is used to tell HASS to access the Wemo switch 97MyMessage msgHASS(HASS_BOOT_CHILD_ID, V_TRIPPED); // HASS is up message which shortcuts timer loop 98MyMessage msgSysState(SYS_STATE_CHILD_ID, V_TEXT); // like it says, this reflects the current state of the system 99 100bool powerState = true; 101bool oldPowerState; // used in the main loop to determine if the powerState has changed 102bool sendPowerState; // used to control the sending of the power state to HA 103bool PCshutdownState = false; // the notification state when the HA should shutdown the RPi 104bool firstLoop = true; // forces a refresh during first loop 105bool haveReset = true; 106bool PCTransFlag = false; // this is true when the PC is transitioning (booting or shutting down) 107bool oldPCTransFlag = false; // used to limit sends to HA 108bool RPITransFlag = false; // this is true during the time when the RPi is transitioning 109bool oldRPITransFlag = false; // used to limit sends to HA 110bool updateFlag = false; // used to trigger active updates (the times when the system is 111bool counterFlag = false; // determines whether or not the countup timer is active 112bool shutdownDelayFlag = false; // used to keep track of the shutdown delay period 113bool RPiBootPrintFlag = true; // gets ready to print out RPi boot message only once 114 115// The three flags below are used in the routines that confirm HA has received an automation triggering event 116bool HassBootedFlag = false; // used to tell when HASS has booted (check by turning on the Wemo switch, thus booting the PC) 117bool PCTransFlagRecieved = false; // used to insure HASS will run automation to shutdown Windows10 118bool RPiTransFlagRecieved = false; // used to insure HASS will run automation to shutdown RPi 119 120// 121uint16_t powerVoltageLevel = 0; // reading of voltage at system power input (reflects AC state) 122uint16_t oldpowerVoltageLevel = 0; 123unsigned long lastrefreshTime = millis();// time in milliseconds of last update: update every REFRESH_TIME 124unsigned long lastCountDownTime = millis(); // used for inloop timers 125int countTime = 0; 126int oldCountTime = 0; // used in the Refresh routine to limit repeat sends to HA 127float voltage = 0.00; // setup variable for analog output to HASS 128float oldVoltage = 0.00; // used by Refresh to limit repeat sending of data to HA 129enum {powerLossed, powerRestored, systemOff, systemOn} sysState; 130int oldSysState; // used to limit sends to HA 131 132void before() { 133 Serial.begin(115200); 134 pinMode(RELAY_PIN, OUTPUT); 135 Serial.print(NAME); Serial.print(" Version "); Serial.println(VERSION); 136 Serial.println("booting RPi for the first time"); 137 digitalWrite(RELAY_PIN, RELAY_ON); // turn on relay when Arduino does a cold start. Boots Raspberry 138} 139 140// boot stalls here while MySensors waits for the RPi to boot 141 142void setup() 143{ 144} 145 146void presentation() { 147 // Send the sketch version information to the gateway and Controller 148 sendSketchInfo(NAME, VERSION); 149 150 // Register all sensors to gw (they will be created as child devices) 151 //present(RELAY_CHILD_ID,S_LIGHT); // controls power to Raspberry 152 present(AC_POWER_STATE_CHILD_ID, S_DOOR); // use S_DOOR to get binary sensor output 153 present(TIMER_CHILD_ID, S_LIGHT_LEVEL, "Current Count"); // current timer count 154 present(POWER_VOLTAGE_CHILD_ID, S_MULTIMETER, "Input Voltage Level"); 155 present(PC_TRANSITION_CHILD_ID, S_DOOR); 156 present(RPI_TRANSITION_CHILD_ID, S_DOOR); 157 present(WEMO_CHILD_ID, S_DOOR); 158 present(HASS_BOOT_CHILD_ID, S_DOOR); 159 present(SYS_STATE_CHILD_ID, S_INFO); 160} 161 162void loop() 163{ 164 getVoltage(); // keep an eye on the voltage, afterall, this is the purpose. This function sets the powerState variable! 165 if (firstLoop) { // setting oldPowerState this way will force execution of if statement below 166 // at this point, the RPi has booted to HASS 167 send(msgHASS.set(false)); // this is needed to make sure that HASS registers this binary sensor 168 oldPowerState = powerState; // will bypass next if statement 169 sysState = powerRestored; // this will boot system. The only way to get here is on reboot of the Arduino 170 counterFlag = false; 171 refresh(); // make sure HA reflects system off condition 172 } 173 if (oldPowerState != powerState) { // state change 174 //let's first check to see if the system is booting or shutting down. If so, wait (no action) 175 // let's determine the new system state 176 // this simple code takes care of a power change during the time the system is booting or shutting down by 177 // allowing the current state (booting or shutting down) to complete, and then changing the state to match 178 // with the power condition. 179 if (powerState && sysState == systemOff) { // power is on but system is off. Turn it on! 180 sysState = powerRestored; 181 oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down 182 refresh(); // make sure HA reflects system off condition 183 delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition 184 } 185 else if (!powerState && sysState == systemOn){ // power is off but system is on. Turn it off! 186 sysState = powerLossed; 187 oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down 188 refresh(); // make sure HA reflects system off condition 189 delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition 190 } 191 } 192// the above happens when the powerState changes, the below continues countdowns and updates counter while counterFlag is true. 193 194 switch (sysState) { 195 case powerLossed: // we are currently starting or doing a countdown to turn off the PC and the Raspbery 196 if(!PCTransFlag && !RPITransFlag && !shutdownDelayFlag){ // first time through loop this will be true 197 shutdownDelayFlag = true; 198 Serial.println("Shutdown delay initiated"); // start PC shutdown process 199 lastCountDownTime = millis(); 200 updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 201 counterFlag = true; // get the countup clock running 202 } 203 if (shutdownDelayFlag) { 204 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 205 if (millis() - lastCountDownTime > SHUTDOWN_DELAY * 60000) {// time has run out, must start shutdown delay 206 shutdownDelayFlag = false; 207 PCTransFlag = true; 208 lastCountDownTime = millis(); 209 Serial.println("PC shutdown initiated"); // start PC shutdown process 210 } 211 else if (powerState && sysState == powerLossed) { // we are shutting down the system, but the power just went back on 212 sysState = systemOn; // the system state is still on, it hasn't been turned off yet. Reseting sysState will exit loop 213 oldPowerState = powerState; // get set for next state change 214 shutdownDelayFlag = false; 215 updateFlag = false; 216 counterFlag = false; 217 refresh(); // reflect new state immediately 218 Serial.println("Shutdown process terminated"); // abort shutdown process 219 break; // exit switch/case 220 } 221 } 222 if (PCTransFlag) { // shutdown Windows10 and turn off the PC 223 if (!PCTransFlagRecieved) { // HA did not catch the transition of PC_TRANSITION_CHILD_ID and thus did not run the 224 // automation to shut down Windows10. So, we have to try to trigger it again 225 // If this never goes, the below shutdown countdown will just pull the plug on the PC 226 oldPCTransFlag = false; PCTransFlag = true; // this will force refresh() to send(msgPCTransition.set(PCTransFlag)) 227 } 228 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 229 if (millis() - lastCountDownTime > PC_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC 230 // and start Raspberry shutdown 231 Serial.println("PC shutdown complete, power disconnected"); 232 PCTransFlag = false; 233 RPITransFlag = true; 234 lastCountDownTime = millis(); 235 send(msgWemo.set(false)); // tells HA to turn on WEMO switch to PC 236 Serial.println("Shutting down HA and RPi"); 237 } 238 } 239 if(RPITransFlag) { // shut down Hassio and turn off the RPi 240 if (!RPiTransFlagRecieved) { // HA did not catch the transition of RPI_TRANSITION_CHILD_ID and thus did not run the 241 // automation to shut down Hassio. So, we have to try to trigger it again 242 // If this never goes, the below shutdown countdown will just pull the plug on the RPi - awful! 243 oldRPITransFlag = false; RPITransFlag = true; // this will force refresh() to send(msgRPITransition.set(RPiTransFlag)) 244 } 245 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 246 if (millis() - lastCountDownTime > RPI_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC 247 digitalWrite(RELAY_PIN, RELAY_OFF); // turn off power to Raspberry, it should already be shutdown 248 Serial.println("RPi turned off"); 249 RPITransFlag = false; 250 sysState = systemOff; 251 updateFlag = false; // stop 1 second updates 252 counterFlag = false; 253 HassBootedFlag = false; // reset this and below for when power returns 254 PCTransFlagRecieved = false; 255 RPiTransFlagRecieved = false; 256 Serial.println("System Shutdown"); 257 } 258 } 259 break; 260 261 case powerRestored: 262 if(!RPITransFlag && !PCTransFlag) { // first time through loop this will be true 263 digitalWrite(RELAY_PIN, RELAY_ON); // turn on power to Raspberry, it should reboot 264 // no point in notifying HA at this point 265 HassBootedFlag = false; // set this false for periodic checks on each loop to see if HA is live 266 RPITransFlag = true; 267 PCTransFlag = false; // make sure this is off during RPi boot 268 Serial.println("power just went on. RPi boot initiated"); 269 lastCountDownTime = millis(); 270 counterFlag = true; // start countup timer 271 } 272 if(RPITransFlag) { 273 if(firstLoop) { // the Raspberry has already booted, no need to wait here! 274 Serial.println("RPi already on!"); 275 RPITransFlag = false; // RPI has booted already 276 PCTransFlag = true; // time to move on to booting the PC, move to next if statement 277 lastCountDownTime = millis(); 278 refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 279 send(msgWemo.set(true)); // tells HA to turn on WEMO switch to PC 280 firstLoop = false; 281 } 282 else { 283 if(RPiBootPrintFlag) { // will print only once based on this flag 284 Serial.println("Must delay while RPi boots."); 285 RPiBootPrintFlag = false; 286 } 287 if (HassBootedFlag) {// raspberry has booted, must boot PC, Wemo switch is turned on during refreshes 288 // which double as a check to see if HA is live 289 Serial.println("RPi has booted!"); 290 RPITransFlag = false; // RPI has booted already 291 PCTransFlag = true; 292 RPiBootPrintFlag = true; // reset this flag for next cycle 293 lastCountDownTime = millis(); 294 refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 295 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 296 } 297 } 298 } 299 if (PCTransFlag) { 300 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 301 if (millis() - lastCountDownTime > PC_BOOT_TIME * 60000) {// time has run out, must disconnect power from the PC 302 // and start Raspberry shutdown 303 Serial.println("PCs should be running"); 304 PCTransFlag = false; 305 sysState = systemOn; // this causes an exit of this case and moves to systemOn, an idle state. 306 updateFlag = false; // stop 1 second updates 307 counterFlag = false; // turn off countup timer 308 countTime = 0; 309 refresh(); 310 Serial.println("System Booted"); 311 } 312 } 313 break; 314 case systemOff: 315 break; 316 case systemOn: 317 break; 318 } 319 //Serial.print("millis() - lastrefreshTime = ");Serial.println(millis() - lastrefreshTime); 320 //Serial.print("update time = ");Serial.println(updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME); 321 if (millis() < lastrefreshTime) {lastrefreshTime = 0; } // clock rolled over 322 if ((millis() - lastrefreshTime) > ((updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME) * 1000)) { 323 lastrefreshTime = millis(); 324 if (counterFlag) { 325 countTime = int((millis()-lastCountDownTime)/1000); 326 } 327 else { 328 countTime = 0; // if we are not counting a state change process, make sure the counter reads 0 329 } 330 if (!HassBootedFlag && RPITransFlag) { // if true, let's check to see if HA is responding 331 send(msgWemo.set(true), true); // this not only turns on the Wemo, but asks for an ACK from HA 332 } 333 refresh(); 334 //Serial.print("SystState = ");Serial.println(sysState); 335 //Serial.print("countTime = "); Serial.println(countTime); 336 } 337} 338 339void receive(const MyMessage &message) { 340 byte relayPinIndex; 341 342 if (message.isAck()) { 343 Serial.println("This is an ack from gateway"); 344 Serial.print("sensor = ");Serial.println(message.sensor); 345 if(message.sensor == WEMO_CHILD_ID) { // using the Wemo sensor(14) to both check for HA and turn on PC 346 HassBootedFlag = true; // The RPi and HA are up and running! 347 Serial.print("ACK for Wemo, it's "); Serial.println(message.getBool() ? "On" : "Off" ); 348 } 349 if(message.sensor == PC_TRANSITION_CHILD_ID) { // this message says that HA knows to run the PC shutdown or boot automations 350 PCTransFlagRecieved = true; // We have an ACK from gateway on the PCTransFlag message. 351 Serial.print("PCTransFlag = "); Serial.println(message.getBool() ? "true" : "false" ); 352 } 353 if(message.sensor == RPI_TRANSITION_CHILD_ID) { // this message says that HA knows to run the RPi shutdown automation 354 RPiTransFlagRecieved = true; // We have an ACK from gateway on the RPiTransFlag message. 355 Serial.print("RPiTransFlag = "); Serial.println(message.getBool() ? "true" : "false" ); 356 } 357 } 358} 359 360void refresh() { 361 getVoltage(); 362 // below, only update on transitions 363 if(PCTransFlag != oldPCTransFlag){send(msgPCTransition.set(PCTransFlag),true); oldPCTransFlag = PCTransFlag;} // use to tell HA to shut down PC 364 if(RPITransFlag != oldRPITransFlag){send(msgRPITransition.set(RPITransFlag),true); oldRPITransFlag = RPITransFlag;} // use to tell HA to shut down itself and RPi 365 // below, if updateFlag is true, update only changes, if false, update all 366 if(powerState != sendPowerState || !updateFlag) {send(msgSense.set(powerState)); sendPowerState = powerState;} 367 if(countTime != oldCountTime || !updateFlag) {send(msgTimer.set(countTime)); oldCountTime = countTime;} // current countdown time in seconds 368 if(voltage != oldVoltage || !updateFlag) {send(msgAO3.set(voltage,2)); oldVoltage = voltage;} 369 if(sysState != oldSysState || !updateFlag) { 370 oldSysState = sysState; 371 switch (sysState) { 372 case powerLossed: 373 send(msgSysState.set("powerLossed")); 374 break; 375 case powerRestored: 376 send(msgSysState.set("powerRestored")); 377 break; 378 case systemOff: 379 send(msgSysState.set("systemOff")); 380 break; 381 case systemOn: 382 send(msgSysState.set("systemOn")); 383 break; 384 } 385 } 386} 387 388void getVoltage() { 389 powerVoltageLevel = analogRead(POWER_VOLTAGE_SENSE_PIN); 390 //Serial.print("powerVoltageLevel = "); Serial.println(powerVoltageLevel); 391 voltage = 5.00 * powerVoltageLevel / 998; // Arduino input does not pull up but does have a series resistor between input pin and Arduino A7 392 if(powerVoltageLevel > 100) { powerState = true;} else {powerState = false;} 393 //Serial.print("voltage = "); Serial.println(voltage); 394} 395
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
1################################################################################ 2########### Power Monitor Automations ########################################## 3 4 - alias: "Power Loss Alert" 5 trigger: 6 - platform: state 7 entity_id: binary_sensor.home_pwr_detection_monito_14_2 8 from: 'on' 9 to: 'off' 10 action: 11 - service: notify.gmail 12 data: 13 message: "Sharon Power Outage" 14 title: "Power Alert" 15 16 - alias: "Power Returned Alert" 17 trigger: 18 - platform: state 19 entity_id: binary_sensor.home_pwr_detection_monito_14_2 20 to: 'on' 21 action: 22 - service: notify.gmail 23 data: 24 message: "Sharon Power Returned" 25 title: "Power Alert" 26 27 - alias: "Shut down PC" 28 trigger: 29 - platform: state 30 entity_id: binary_sensor.home_pwr_detection_monito_14_12 31 from: 'off' 32 to: 'on' 33 condition: 34 - condition: state 35 entity_id: "sensor.home_pwr_detection_monito_14_16" 36 state: "powerLossed" 37 action: 38 - service: notify.gmail 39 data: 40 message: "Sharon Power Outage" 41 title: "PC Shutdown Initiated" 42# - service: hassio.addon_stdin 43# data: 44# addon: core_rpc_shutdown 45# input: Sharon_HP 46 47 - alias: "Boot PC" 48 trigger: 49 - platform: state 50 entity_id: binary_sensor.home_pwr_detection_monito_14_12 51 from: 'off' 52 to: 'on' 53 condition: 54 - condition: state 55 entity_id: "binary_sensor.home_pwr_detection_monito_14_16" 56 state: "powerRestored" 57 action: 58 - service: notify.gmail 59 data: 60 message: "Sharon Power Outage" 61 title: "PC Boot Up Initiated" 62 63 - alias: "Shut down RPi" 64 trigger: 65 - platform: state 66 entity_id: binary_sensor.home_pwr_detection_monito_14_13 67 from: 'off' 68 to: 'on' 69 condition: 70 - condition: state 71 entity_id: "sensor.home_pwr_detection_monito_14_16" 72 state: "powerLossed" 73 action: 74 - service: notify.gmail 75 data: 76 message: "Sharon Power Outage" 77 title: "Raspbery Shutdown Initiated" 78 # - service: hassio.host_shutdown 79 80 - alias: "Boot RPi" 81 trigger: 82 - platform: state 83 entity_id: binary_sensor.home_pwr_detection_monito_14_13 84 from: 'off' 85 to: 'on' 86 condition: 87 - condition: state 88 entity_id: "sensor.home_pwr_detection_monito_14_16" 89 state: "powerRestored" 90 action: 91 - service: notify.gmail 92 data: 93 message: "Sharon Power Outage" 94 title: "Raspbery Boot Up Initiated" 95 96 - alias: "PC Wemo Power Off" 97 trigger: 98 - platform: state 99 entity_id: binary_sensor.home_pwr_detection_monito_14_14 100 from: "on" 101 to: "off" 102 action: 103 - service: notify.gmail 104 data: 105 message: "Sharon PC Power Turned off" 106 title: "PC Power" 107 - service: homeassistant.turn_off 108 entity_id: switch.small_wemo_1 # this is actually the HP power switch 109 110 - alias: "PC Wemo Power On" 111 trigger: 112 - platform: state 113 entity_id: binary_sensor.home_pwr_detection_monito_14_14 114 from: "off" 115 to: "on" 116 action: 117 - service: notify.gmail 118 data: 119 message: "Sharon PC Power Turned on" 120 title: "PC Power" 121 - service: homeassistant.turn_on 122 entity_id: switch.small_wemo_1 # this is actually the HP power switch 123 124 - alias: "RPi Boot Message" 125 trigger: 126 - platform: homeassistant 127 event: start 128 action: 129 service: python_script.set_state 130 data_template: 131 entity_id: binary_sensor.home_pwr_detection_monito_14_15 132 state: 'on'
Power Monitor Arduino Code v4.1
c_cpp
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.
1/** 2 * Home Power Detection Monitor 3 * The purpose of this device is to indicate when there is a power loss. It uses a circuit originally designed as a backup system, 4 * 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 5 * home's AC power source. The circuit senses the voltage from the 5 volt supply as a digital signal (connected to D3). 6 * At AC power failure: the UPS provides power to the circuit. The circuit senses the power loss and sends a signal to Homeassistant. 7 * When AC power is available, the circuit is once again powered by it. 8 * 9 * The Home Assistant timer input sets the time in minutes until Home Assistant initiates it's own shut down. After Home Assistant has 10 * shut down, the Arduino turns off the relay that provides power to the Raspberry. One of two possibilities will occur subsequently. 11 * 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 12 * the Raspberry, rebooting the system. 13 * 2) if the APS power dies before the utility power returns, then when the utility power does return, the Arduino delays for a preset 14 * amount of time to insure power stability, then applies power to the Raspberry, which reboots. 15 * 16 * Upon power failure, HomeAssistant sends an alert to email. Home Assistant will also shut down the PCs on the network. Once 17 * everything is shut down, Home Assistant will send a command to the Arduino, to turn off the power to the Raspberry (which is running 18 * home assistant). The execution of the command is delayed for RPI_SHUTDOWN_TIME (see below). During this time, Home Assistant will 19 * shut down itself and HASSIO (the operating system). Some time later, the Arduino completes its time out (RPI_SHUTDOWN_TIME) and 20 * turns off the power to the Raspberry using the Relay. 21 * 22 * 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 23 * 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 24 * still providing power upon return of AC power, the Arduino will still be running. When it detects that the external power has returned, 25 * it will wait for (RPI_SHUTDOWN_TIME) before applying power to the Raspberry. This is to avoid attempting to restart the Raspberry 26 * 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 27 * to reboot as well. 28 * 29 * By removing the PCs from the APC UPS, the power requirement for the UPS will be drastically reduced, extending its up time. Hopefully, 30 * this would be longer than any power outage. For Sharon, there are two pcs and two UPSs; one for the network router and associated 31 * equipment, and one for the Raspberry and office pc. 32 * 33 * The MySensors Arduino library handles the wireless radio link and protocol 34 * between your home built sensors/actuators and HA controller of choice. 35 * The sensors forms a self healing radio network with optional repeaters. Each 36 * repeater and gateway builds a routing tables in EEPROM which keeps track of the 37 * network topology allowing messages to be routed to nodes. 38 * 39 * Created by Henrik Ekblad <henrik.ekblad@mysensors.org> 40 * Copyright (C) 2013-2015 Sensnology AB 41 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 42 * 43 * Documentation: http://www.mysensors.org 44 * Support Forum: http://forum.mysensors.org 45 * 46 * This program is free software; you can redistribute it and/or 47 * modify it under the terms of the GNU General Public License 48 * version 2 as published by the Free Software Foundation. 49 * 50 ******************************* 51 * REVISION HISTORY 52 * Version 1.0 - Henrik Ekblad 53 * Heavily modified by Paul Zavracky, October 2020 54 * See version number below 55 */ 56#define VERSION "4.1" 57#define NAME "HOME_PWR_DETECTION_MONITOR" 58 59// Enable debug prints to serial monitor 60#define MY_DEBUG 61 62// Enable and select radio type attached 63#define MY_RADIO_RF24 64#define MY_NODE_ID 14 65 66#include <MySensors.h> 67 68#define RELAY_PIN 8 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) 69#define POWER_VOLTAGE_SENSE_PIN A7 // Arduino pin used to sense the external 110VAC power source. If this goes low, we've lost power. 70 71#define AC_POWER_STATE_CHILD_ID 2 // ID of power sense pin (takes input from a 5v supply plugged into AC power source). 72#define TIMER_CHILD_ID 4 // this is used for the countdown timer. It tell HA the number of seconds on the clock. 73#define POWER_VOLTAGE_CHILD_ID 11 // leaving room for LED light readings - not really necessary 74#define PC_TRANSITION_CHILD_ID 12 // this output tells HASS to shutdown the PCs 75#define RPI_TRANSITION_CHILD_ID 13 // this output tells HASS to shutdown the RPi 76#define WEMO_CHILD_ID 14 // this is used to tell HASS to access the Wemo switch 77#define HASS_BOOT_CHILD_ID 15 // this message will let the Arduino know that HASS has booted (no longer used) 78#define SYS_STATE_CHILD_ID 16 // this is text representing the state of the system 79 80#define SHUTDOWN_DELAY .2 // time in minutes before a shutdown occurs in response to a power loss 81#define REFRESH_TIME 12 // time in seconds between full data refreshes during idle periods 82#define ACTIVE_UPDATE_TIME 1 // when in shutdown or startup mode, this is the update rate in seconds 83#define RPI_SHUTDOWN_TIME .2 // time in minutes to allow Raspberry to shutdown before switching off power 84#define PC_BOOT_TIME .2 // time in minutes to allow the PC to boot. Actual boot time includes booting the RPi 85#define PC_SHUTDOWN_TIME .2 // time in minutes to allow PCs to shutdown before switching off power 86 87#define RELAY_ON 1 88#define RELAY_OFF 0 89 90//MyMessage msgRelay(RELAY_CHILD_ID, V_TRIPPED); // set up switch for relay - we don't need this 91MyMessage msgSense(AC_POWER_STATE_CHILD_ID, V_TRIPPED); // set up as binary sensor 92MyMessage msgTimer(TIMER_CHILD_ID, V_LEVEL); // used to display countdown timer 93MyMessage msgAO3(POWER_VOLTAGE_CHILD_ID, V_VOLTAGE); // AC Power Voltage Indicator 94MyMessage msgPCTransition(PC_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down PC 95MyMessage msgRPITransition(RPI_TRANSITION_CHILD_ID, V_TRIPPED); // use to tell HA to shut down itself and RPi 96MyMessage msgWemo(WEMO_CHILD_ID, V_TRIPPED); // this is used to tell HASS to access the Wemo switch 97MyMessage msgHASS(HASS_BOOT_CHILD_ID, V_TRIPPED); // HASS is up message which shortcuts timer loop 98MyMessage msgSysState(SYS_STATE_CHILD_ID, V_TEXT); // like it says, this reflects the current state of the system 99 100bool powerState = true; 101bool oldPowerState; // used in the main loop to determine if the powerState has changed 102bool sendPowerState; // used to control the sending of the power state to HA 103bool PCshutdownState = false; // the notification state when the HA should shutdown the RPi 104bool firstLoop = true; // forces a refresh during first loop 105bool haveReset = true; 106bool PCTransFlag = false; // this is true when the PC is transitioning (booting or shutting down) 107bool oldPCTransFlag = false; // used to limit sends to HA 108bool RPITransFlag = false; // this is true during the time when the RPi is transitioning 109bool oldRPITransFlag = false; // used to limit sends to HA 110bool updateFlag = false; // used to trigger active updates (the times when the system is 111bool counterFlag = false; // determines whether or not the countup timer is active 112bool shutdownDelayFlag = false; // used to keep track of the shutdown delay period 113bool RPiBootPrintFlag = true; // gets ready to print out RPi boot message only once 114 115// The three flags below are used in the routines that confirm HA has received an automation triggering event 116bool HassBootedFlag = false; // used to tell when HASS has booted (check by turning on the Wemo switch, thus booting the PC) 117bool PCTransFlagRecieved = false; // used to insure HASS will run automation to shutdown Windows10 118bool RPiTransFlagRecieved = false; // used to insure HASS will run automation to shutdown RPi 119 120// 121uint16_t powerVoltageLevel = 0; // reading of voltage at system power input (reflects AC state) 122uint16_t oldpowerVoltageLevel = 0; 123unsigned long lastrefreshTime = millis();// time in milliseconds of last update: update every REFRESH_TIME 124unsigned long lastCountDownTime = millis(); // used for inloop timers 125int countTime = 0; 126int oldCountTime = 0; // used in the Refresh routine to limit repeat sends to HA 127float voltage = 0.00; // setup variable for analog output to HASS 128float oldVoltage = 0.00; // used by Refresh to limit repeat sending of data to HA 129enum {powerLossed, powerRestored, systemOff, systemOn} sysState; 130int oldSysState; // used to limit sends to HA 131 132void before() { 133 Serial.begin(115200); 134 pinMode(RELAY_PIN, OUTPUT); 135 Serial.print(NAME); Serial.print(" Version "); Serial.println(VERSION); 136 Serial.println("booting RPi for the first time"); 137 digitalWrite(RELAY_PIN, RELAY_ON); // turn on relay when Arduino does a cold start. Boots Raspberry 138} 139 140// boot stalls here while MySensors waits for the RPi to boot 141 142void setup() 143{ 144} 145 146void presentation() { 147 // Send the sketch version information to the gateway and Controller 148 sendSketchInfo(NAME, VERSION); 149 150 // Register all sensors to gw (they will be created as child devices) 151 //present(RELAY_CHILD_ID,S_LIGHT); // controls power to Raspberry 152 present(AC_POWER_STATE_CHILD_ID, S_DOOR); // use S_DOOR to get binary sensor output 153 present(TIMER_CHILD_ID, S_LIGHT_LEVEL, "Current Count"); // current timer count 154 present(POWER_VOLTAGE_CHILD_ID, S_MULTIMETER, "Input Voltage Level"); 155 present(PC_TRANSITION_CHILD_ID, S_DOOR); 156 present(RPI_TRANSITION_CHILD_ID, S_DOOR); 157 present(WEMO_CHILD_ID, S_DOOR); 158 present(HASS_BOOT_CHILD_ID, S_DOOR); 159 present(SYS_STATE_CHILD_ID, S_INFO); 160} 161 162void loop() 163{ 164 getVoltage(); // keep an eye on the voltage, afterall, this is the purpose. This function sets the powerState variable! 165 if (firstLoop) { // setting oldPowerState this way will force execution of if statement below 166 // at this point, the RPi has booted to HASS 167 send(msgHASS.set(false)); // this is needed to make sure that HASS registers this binary sensor 168 oldPowerState = powerState; // will bypass next if statement 169 sysState = powerRestored; // this will boot system. The only way to get here is on reboot of the Arduino 170 counterFlag = false; 171 refresh(); // make sure HA reflects system off condition 172 } 173 if (oldPowerState != powerState) { // state change 174 //let's first check to see if the system is booting or shutting down. If so, wait (no action) 175 // let's determine the new system state 176 // this simple code takes care of a power change during the time the system is booting or shutting down by 177 // allowing the current state (booting or shutting down) to complete, and then changing the state to match 178 // with the power condition. 179 if (powerState && sysState == systemOff) { // power is on but system is off. Turn it on! 180 sysState = powerRestored; 181 oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down 182 refresh(); // make sure HA reflects system off condition 183 delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition 184 } 185 else if (!powerState && sysState == systemOn){ // power is off but system is on. Turn it off! 186 sysState = powerLossed; 187 oldPowerState = powerState; // can't change this unless the system was in an idle state - booted or shut down 188 refresh(); // make sure HA reflects system off condition 189 delay(1000); // this delay is intended to make sure HA sees the transition of state if powerState changes during transition 190 } 191 } 192// the above happens when the powerState changes, the below continues countdowns and updates counter while counterFlag is true. 193 194 switch (sysState) { 195 case powerLossed: // we are currently starting or doing a countdown to turn off the PC and the Raspbery 196 if(!PCTransFlag && !RPITransFlag && !shutdownDelayFlag){ // first time through loop this will be true 197 shutdownDelayFlag = true; 198 Serial.println("Shutdown delay initiated"); // start PC shutdown process 199 lastCountDownTime = millis(); 200 updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 201 counterFlag = true; // get the countup clock running 202 } 203 if (shutdownDelayFlag) { 204 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 205 if (millis() - lastCountDownTime > SHUTDOWN_DELAY * 60000) {// time has run out, must start shutdown delay 206 shutdownDelayFlag = false; 207 PCTransFlag = true; 208 lastCountDownTime = millis(); 209 Serial.println("PC shutdown initiated"); // start PC shutdown process 210 } 211 else if (powerState && sysState == powerLossed) { // we are shutting down the system, but the power just went back on 212 sysState = systemOn; // the system state is still on, it hasn't been turned off yet. Reseting sysState will exit loop 213 oldPowerState = powerState; // get set for next state change 214 shutdownDelayFlag = false; 215 updateFlag = false; 216 counterFlag = false; 217 refresh(); // reflect new state immediately 218 Serial.println("Shutdown process terminated"); // abort shutdown process 219 break; // exit switch/case 220 } 221 } 222 if (PCTransFlag) { // shutdown Windows10 and turn off the PC 223 if (!PCTransFlagRecieved) { // HA did not catch the transition of PC_TRANSITION_CHILD_ID and thus did not run the 224 // automation to shut down Windows10. So, we have to try to trigger it again 225 // If this never goes, the below shutdown countdown will just pull the plug on the PC 226 oldPCTransFlag = false; PCTransFlag = true; // this will force refresh() to send(msgPCTransition.set(PCTransFlag)) 227 } 228 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 229 if (millis() - lastCountDownTime > PC_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC 230 // and start Raspberry shutdown 231 Serial.println("PC shutdown complete, power disconnected"); 232 PCTransFlag = false; 233 RPITransFlag = true; 234 lastCountDownTime = millis(); 235 send(msgWemo.set(false)); // tells HA to turn on WEMO switch to PC 236 Serial.println("Shutting down HA and RPi"); 237 } 238 } 239 if(RPITransFlag) { // shut down Hassio and turn off the RPi 240 if (!RPiTransFlagRecieved) { // HA did not catch the transition of RPI_TRANSITION_CHILD_ID and thus did not run the 241 // automation to shut down Hassio. So, we have to try to trigger it again 242 // If this never goes, the below shutdown countdown will just pull the plug on the RPi - awful! 243 oldRPITransFlag = false; RPITransFlag = true; // this will force refresh() to send(msgRPITransition.set(RPiTransFlag)) 244 } 245 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 246 if (millis() - lastCountDownTime > RPI_SHUTDOWN_TIME * 60000) {// time has run out, must disconnect power from the PC 247 digitalWrite(RELAY_PIN, RELAY_OFF); // turn off power to Raspberry, it should already be shutdown 248 Serial.println("RPi turned off"); 249 RPITransFlag = false; 250 sysState = systemOff; 251 updateFlag = false; // stop 1 second updates 252 counterFlag = false; 253 HassBootedFlag = false; // reset this and below for when power returns 254 PCTransFlagRecieved = false; 255 RPiTransFlagRecieved = false; 256 Serial.println("System Shutdown"); 257 } 258 } 259 break; 260 261 case powerRestored: 262 if(!RPITransFlag && !PCTransFlag) { // first time through loop this will be true 263 digitalWrite(RELAY_PIN, RELAY_ON); // turn on power to Raspberry, it should reboot 264 // no point in notifying HA at this point 265 HassBootedFlag = false; // set this false for periodic checks on each loop to see if HA is live 266 RPITransFlag = true; 267 PCTransFlag = false; // make sure this is off during RPi boot 268 Serial.println("power just went on. RPi boot initiated"); 269 lastCountDownTime = millis(); 270 counterFlag = true; // start countup timer 271 } 272 if(RPITransFlag) { 273 if(firstLoop) { // the Raspberry has already booted, no need to wait here! 274 Serial.println("RPi already on!"); 275 RPITransFlag = false; // RPI has booted already 276 PCTransFlag = true; // time to move on to booting the PC, move to next if statement 277 lastCountDownTime = millis(); 278 refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 279 send(msgWemo.set(true)); // tells HA to turn on WEMO switch to PC 280 firstLoop = false; 281 } 282 else { 283 if(RPiBootPrintFlag) { // will print only once based on this flag 284 Serial.println("Must delay while RPi boots."); 285 RPiBootPrintFlag = false; 286 } 287 if (HassBootedFlag) {// raspberry has booted, must boot PC, Wemo switch is turned on during refreshes 288 // which double as a check to see if HA is live 289 Serial.println("RPi has booted!"); 290 RPITransFlag = false; // RPI has booted already 291 PCTransFlag = true; 292 RPiBootPrintFlag = true; // reset this flag for next cycle 293 lastCountDownTime = millis(); 294 refresh(); updateFlag = true; // causes update every ACTIVE_UPDATE_TIME 295 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 296 } 297 } 298 } 299 if (PCTransFlag) { 300 if (millis() < lastCountDownTime) {lastCountDownTime = 0; } // clock rolled over 301 if (millis() - lastCountDownTime > PC_BOOT_TIME * 60000) {// time has run out, must disconnect power from the PC 302 // and start Raspberry shutdown 303 Serial.println("PCs should be running"); 304 PCTransFlag = false; 305 sysState = systemOn; // this causes an exit of this case and moves to systemOn, an idle state. 306 updateFlag = false; // stop 1 second updates 307 counterFlag = false; // turn off countup timer 308 countTime = 0; 309 refresh(); 310 Serial.println("System Booted"); 311 } 312 } 313 break; 314 case systemOff: 315 break; 316 case systemOn: 317 break; 318 } 319 //Serial.print("millis() - lastrefreshTime = ");Serial.println(millis() - lastrefreshTime); 320 //Serial.print("update time = ");Serial.println(updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME); 321 if (millis() < lastrefreshTime) {lastrefreshTime = 0; } // clock rolled over 322 if ((millis() - lastrefreshTime) > ((updateFlag ? ACTIVE_UPDATE_TIME : REFRESH_TIME) * 1000)) { 323 lastrefreshTime = millis(); 324 if (counterFlag) { 325 countTime = int((millis()-lastCountDownTime)/1000); 326 } 327 else { 328 countTime = 0; // if we are not counting a state change process, make sure the counter reads 0 329 } 330 if (!HassBootedFlag && RPITransFlag) { // if true, let's check to see if HA is responding 331 send(msgWemo.set(true), true); // this not only turns on the Wemo, but asks for an ACK from HA 332 } 333 refresh(); 334 //Serial.print("SystState = ");Serial.println(sysState); 335 //Serial.print("countTime = "); Serial.println(countTime); 336 } 337} 338 339void receive(const MyMessage &message) { 340 byte relayPinIndex; 341 342 if (message.isAck()) { 343 Serial.println("This is an ack from gateway"); 344 Serial.print("sensor = ");Serial.println(message.sensor); 345 if(message.sensor == WEMO_CHILD_ID) { // using the Wemo sensor(14) to both check for HA and turn on PC 346 HassBootedFlag = true; // The RPi and HA are up and running! 347 Serial.print("ACK for Wemo, it's "); Serial.println(message.getBool() ? "On" : "Off" ); 348 } 349 if(message.sensor == PC_TRANSITION_CHILD_ID) { // this message says that HA knows to run the PC shutdown or boot automations 350 PCTransFlagRecieved = true; // We have an ACK from gateway on the PCTransFlag message. 351 Serial.print("PCTransFlag = "); Serial.println(message.getBool() ? "true" : "false" ); 352 } 353 if(message.sensor == RPI_TRANSITION_CHILD_ID) { // this message says that HA knows to run the RPi shutdown automation 354 RPiTransFlagRecieved = true; // We have an ACK from gateway on the RPiTransFlag message. 355 Serial.print("RPiTransFlag = "); Serial.println(message.getBool() ? "true" : "false" ); 356 } 357 } 358} 359 360void refresh() { 361 getVoltage(); 362 // below, only update on transitions 363 if(PCTransFlag != oldPCTransFlag){send(msgPCTransition.set(PCTransFlag),true); oldPCTransFlag = PCTransFlag;} // use to tell HA to shut down PC 364 if(RPITransFlag != oldRPITransFlag){send(msgRPITransition.set(RPITransFlag),true); oldRPITransFlag = RPITransFlag;} // use to tell HA to shut down itself and RPi 365 // below, if updateFlag is true, update only changes, if false, update all 366 if(powerState != sendPowerState || !updateFlag) {send(msgSense.set(powerState)); sendPowerState = powerState;} 367 if(countTime != oldCountTime || !updateFlag) {send(msgTimer.set(countTime)); oldCountTime = countTime;} // current countdown time in seconds 368 if(voltage != oldVoltage || !updateFlag) {send(msgAO3.set(voltage,2)); oldVoltage = voltage;} 369 if(sysState != oldSysState || !updateFlag) { 370 oldSysState = sysState; 371 switch (sysState) { 372 case powerLossed: 373 send(msgSysState.set("powerLossed")); 374 break; 375 case powerRestored: 376 send(msgSysState.set("powerRestored")); 377 break; 378 case systemOff: 379 send(msgSysState.set("systemOff")); 380 break; 381 case systemOn: 382 send(msgSysState.set("systemOn")); 383 break; 384 } 385 } 386} 387 388void getVoltage() { 389 powerVoltageLevel = analogRead(POWER_VOLTAGE_SENSE_PIN); 390 //Serial.print("powerVoltageLevel = "); Serial.println(powerVoltageLevel); 391 voltage = 5.00 * powerVoltageLevel / 998; // Arduino input does not pull up but does have a series resistor between input pin and Arduino A7 392 if(powerVoltageLevel > 100) { powerState = true;} else {powerState = false;} 393 //Serial.print("voltage = "); Serial.println(voltage); 394} 395
Set_State.py
c_cpp
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.
1# by Rod Payne 2inputEntity = data.get('entity_id') 3if inputEntity 4 is None: 5 logger.warning("===== entity_id is required if you want to set 6 something.") 7elif hass.states.get(inputEntity) is None: 8 logger.warning("===== 9 unknown entity_id: %s", inputEntity) 10else: 11 inputStateObject = hass.states.get(inputEntity) 12 13 inputState = inputStateObject.state 14 inputAttributesObject = inputStateObject.attributes.copy() 15 16 17 for item in data: 18 newAttribute = data.get(item) 19 logger.debug("===== 20 item = {0}; value = {1}".format(item,newAttribute)) 21 if item == 'entity_id': 22 23 continue # already handled 24 elif item == 'state': 25 26 inputState = newAttribute 27 else: 28 inputAttributesObject[item] 29 = newAttribute 30 31 hass.states.set(inputEntity, inputState, inputAttributesObject)
Lovelace Interface
yaml
This yaml file creates a page for the Power Monitor in Home Assistant.
1 - title: Power Monitor 2 cards: 3 - type: entities 4 title: 5 Power Monitor 6 show_header_toggle: false 7 entities: 8 - 9 entity: binary_sensor.home_pwr_detection_monito_14_2 10 name: Power Condition 11 (booted/shutdown) 12 - entity: sensor.home_pwr_detection_monito_14_16 13 14 name: System State (booted/shutdown) 15 - entity: sensor.home_pwr_detection_monito_14_4 16 17 name: Countup Timer 18 icon: mdi:clock 19 - entity: 20 binary_sensor.home_pwr_detection_monito_14_12 21 name: PC transitioning 22 (on/off)) 23 - entity: binary_sensor.home_pwr_detection_monito_14_13 24 25 name: RPi transitioning (on/off) 26 - entity: binary_sensor.home_pwr_detection_monito_14_14 27 28 name: PC Power Switch (Wemo)
Set_State.py
c_cpp
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.
1# by Rod Payne 2inputEntity = data.get('entity_id') 3if inputEntity is None: 4 logger.warning("===== entity_id is required if you want to set something.") 5elif hass.states.get(inputEntity) is None: 6 logger.warning("===== unknown entity_id: %s", inputEntity) 7else: 8 inputStateObject = hass.states.get(inputEntity) 9 inputState = inputStateObject.state 10 inputAttributesObject = inputStateObject.attributes.copy() 11 12 for item in data: 13 newAttribute = data.get(item) 14 logger.debug("===== item = {0}; value = {1}".format(item,newAttribute)) 15 if item == 'entity_id': 16 continue # already handled 17 elif item == 'state': 18 inputState = newAttribute 19 else: 20 inputAttributesObject[item] = newAttribute 21 22 hass.states.set(inputEntity, inputState, inputAttributesObject)
Lovelace Interface
yaml
This yaml file creates a page for the Power Monitor in Home Assistant.
1 - title: Power Monitor 2 cards: 3 - type: entities 4 title: Power Monitor 5 show_header_toggle: false 6 entities: 7 - entity: binary_sensor.home_pwr_detection_monito_14_2 8 name: Power Condition (booted/shutdown) 9 - entity: sensor.home_pwr_detection_monito_14_16 10 name: System State (booted/shutdown) 11 - entity: sensor.home_pwr_detection_monito_14_4 12 name: Countup Timer 13 icon: mdi:clock 14 - entity: binary_sensor.home_pwr_detection_monito_14_12 15 name: PC transitioning (on/off)) 16 - entity: binary_sensor.home_pwr_detection_monito_14_13 17 name: RPi transitioning (on/off) 18 - entity: binary_sensor.home_pwr_detection_monito_14_14 19 name: PC Power Switch (Wemo)
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
1################################################################################ 2########### Power Monitor Automations ########################################## 3 4 - alias: "Power Loss Alert" 5 trigger: 6 - platform: state 7 entity_id: binary_sensor.home_pwr_detection_monito_14_2 8 from: 'on' 9 to: 'off' 10 action: 11 - service: notify.gmail 12 data: 13 message: "Sharon Power Outage" 14 title: "Power Alert" 15 16 - alias: "Power Returned Alert" 17 trigger: 18 - platform: state 19 entity_id: binary_sensor.home_pwr_detection_monito_14_2 20 to: 'on' 21 action: 22 - service: notify.gmail 23 data: 24 message: "Sharon Power Returned" 25 title: "Power Alert" 26 27 - alias: "Shut down PC" 28 trigger: 29 - platform: state 30 entity_id: binary_sensor.home_pwr_detection_monito_14_12 31 from: 'off' 32 to: 'on' 33 condition: 34 - condition: state 35 entity_id: "sensor.home_pwr_detection_monito_14_16" 36 state: "powerLossed" 37 action: 38 - service: notify.gmail 39 data: 40 message: "Sharon Power Outage" 41 title: "PC Shutdown Initiated" 42# - service: hassio.addon_stdin 43# data: 44# addon: core_rpc_shutdown 45# input: Sharon_HP 46 47 - alias: "Boot PC" 48 trigger: 49 - platform: state 50 entity_id: binary_sensor.home_pwr_detection_monito_14_12 51 from: 'off' 52 to: 'on' 53 condition: 54 - condition: state 55 entity_id: "binary_sensor.home_pwr_detection_monito_14_16" 56 state: "powerRestored" 57 action: 58 - service: notify.gmail 59 data: 60 message: "Sharon Power Outage" 61 title: "PC Boot Up Initiated" 62 63 - alias: "Shut down RPi" 64 trigger: 65 - platform: state 66 entity_id: binary_sensor.home_pwr_detection_monito_14_13 67 from: 'off' 68 to: 'on' 69 condition: 70 - condition: state 71 entity_id: "sensor.home_pwr_detection_monito_14_16" 72 state: "powerLossed" 73 action: 74 - service: notify.gmail 75 data: 76 message: "Sharon Power Outage" 77 title: "Raspbery Shutdown Initiated" 78 # - service: hassio.host_shutdown 79 80 - alias: "Boot RPi" 81 trigger: 82 - platform: state 83 entity_id: binary_sensor.home_pwr_detection_monito_14_13 84 from: 'off' 85 to: 'on' 86 condition: 87 - condition: state 88 entity_id: "sensor.home_pwr_detection_monito_14_16" 89 state: "powerRestored" 90 action: 91 - service: notify.gmail 92 data: 93 message: "Sharon Power Outage" 94 title: "Raspbery Boot Up Initiated" 95 96 - alias: "PC Wemo Power Off" 97 trigger: 98 - platform: state 99 entity_id: binary_sensor.home_pwr_detection_monito_14_14 100 from: "on" 101 to: "off" 102 action: 103 - service: notify.gmail 104 data: 105 message: "Sharon PC Power Turned off" 106 title: "PC Power" 107 - service: homeassistant.turn_off 108 entity_id: switch.small_wemo_1 # this is actually the HP power switch 109 110 - alias: "PC Wemo Power On" 111 trigger: 112 - platform: state 113 entity_id: binary_sensor.home_pwr_detection_monito_14_14 114 from: "off" 115 to: "on" 116 action: 117 - service: notify.gmail 118 data: 119 message: "Sharon PC Power Turned on" 120 title: "PC Power" 121 - service: homeassistant.turn_on 122 entity_id: switch.small_wemo_1 # this is actually the HP power switch 123 124 - alias: "RPi Boot Message" 125 trigger: 126 - platform: homeassistant 127 event: start 128 action: 129 service: python_script.set_state 130 data_template: 131 entity_id: binary_sensor.home_pwr_detection_monito_14_15 132 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.'
1 - alias: "Power Loss Alert" 2 trigger: 3 - platform: state 4 entity_id: binary_sensor.home_pwr_detection_monito_14_2 5 from: 'on' 6 to: 'off' 7 action: 8 - service: notify.gmail 9 data: 10 message: "Sharon Power Outage" 11 title: "Power Alert" 12 13 - alias: "Power Returned Alert" 14 trigger: 15 - platform: state 16 entity_id: binary_sensor.home_pwr_detection_monito_14_2 17 to: 'on' 18 action: 19 - service: notify.gmail 20 data: 21 message: "Sharon Power Returned" 22 title: "Power Alert" 23 24 - alias: "Shut down PC" 25 trigger: 26 - platform: state 27 entity_id: binary_sensor.home_pwr_detection_monito_14_12 28 from: 'off' 29 to: 'on' 30 condition: 31 - condition: state 32 entity_id: "sensor.home_pwr_detection_monito_14_16" 33 state: "powerLossed" 34 action: 35 - service: notify.gmail 36 data: 37 message: "Sharon Power Outage" 38 title: "PC Shutdown Initiated" 39 - service: hassio.addon_stdin 40 data: 41 addon: core_rpc_shutdown 42 input: Sharon_HP 43 44 - alias: "Boot PC" 45 trigger: 46 - platform: state 47 entity_id: binary_sensor.home_pwr_detection_monito_14_12 48 from: 'off' 49 to: 'on' 50 condition: 51 - condition: state 52 entity_id: "binary_sensor.home_pwr_detection_monito_14_16" 53 state: "powerRestored" 54 action: 55 - service: notify.gmail 56 data: 57 message: "Sharon Power Outage" 58 title: "PC Boot Up Initiated" 59 60 - alias: "Shut down RPi" 61 trigger: 62 - platform: state 63 entity_id: binary_sensor.home_pwr_detection_monito_14_13 64 from: 'off' 65 to: 'on' 66 condition: 67 - condition: state 68 entity_id: "sensor.home_pwr_detection_monito_14_16" 69 state: "powerLossed" 70 action: 71 - service: notify.gmail 72 data: 73 message: "Sharon Power Outage" 74 title: "Raspbery Shutdown Initiated" 75 - service: hassio.host_shutdown 76 77 - alias: "Boot RPi" 78 trigger: 79 - platform: state 80 entity_id: binary_sensor.home_pwr_detection_monito_14_13 81 from: 'off' 82 to: 'on' 83 condition: 84 - condition: state 85 entity_id: "sensor.home_pwr_detection_monito_14_16" 86 state: "powerRestored" 87 action: 88 - service: notify.gmail 89 data: 90 message: "Sharon Power Outage" 91 title: "Raspbery Boot Up Initiated" 92 93 - alias: "PC Wemo Power Off" 94 trigger: 95 - platform: state 96 entity_id: binary_sensor.home_pwr_detection_monito_14_14 97 from: "on" 98 to: "off" 99 action: 100 - service: homeassistant.turn_off 101 entity_id: switch.sharon_rpi # this is actually the HP power switch 102 103 - alias: "PC Wemo Power On" 104 trigger: 105 - platform: state 106 entity_id: binary_sensor.home_pwr_detection_monito_14_14 107 from: "off" 108 to: "on" 109 action: 110 - service: homeassistant.turn_on 111 entity_id: switch.sharon_rpi # this is actually the HP power switch 112 113 - alias: "RPi Boot Message" 114 trigger: 115 - platform: homeassistant 116 event: start 117 action: 118 service: python_script.set_state 119 data_template: 120 entity_id: binary_sensor.home_pwr_detection_monito_14_15 121 state: 'on'
Downloadable files
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.
Power Monitor Circuit Board
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.
Power Monitor Circuit Board
Documentation
Power Monitor Enclosure
Power Monitor Enclosure
Power Monitor Enclosure
Power Monitor Enclosure
Power Monitor Cover
Power Monitor Cover
Comments
Only logged in users can leave comments