RPI with HA checks up on RPI with HA

in the past i had a few times trouble with HA on the RPI.
oke, i made a backup image and backup my settings daily and my sensor values every 15 minutes.
i have a spare SD card ready to take over if needed.
but then it still can happen that the RPI stops running when you are not around to reset it.

so i started working on an emergency plan.

there are 2 possible things that go wrong:

  1. HA stops running
  2. the RPI stops running

i havent had 1 time that HA has stopped working so that wasnt my first priority.
so i started to make sure that the RPI keeps running.

there also are 2 options

  1. the RPI stops running, but it can be restarted without any problems
  2. the RPI stops working completely.

the first one is the most easy part to control, so i have created a plan for that.
the second one i am working on.

what i needed to make sure my RPI restart when it stops running:
a) a second RPI (i had an old RPI2 lying around)
b) an arduino or esp (i have a lot of arduino nanos lying around so i used those)
c) a NRF24 tranceiver (i use mysensors for a lot of things, so i have lots of those)
d) a relayboard with 1 relay

i did use mysensors because it is very simple to setup. and there is no need for extra configuring or anything.

i connected it this way:

the cinfiguration.yaml from the backup is quite simple

homeassistant:
  name: backup Home Assistant
  latitude: YOUR_LATITUDE
  longitude: YOUR_LONGTITUDE
  unit_system: metric
  time_zone: Europe/Berlin
  customize: !include customize.yaml
  elevation: 102
  
frontend:

http: 
  api_password: !secret http_password

notify:
  - name: your_notify_name
    platform: pushbullet
    api_key: YOUR_API_KEY

device_tracker:
  - platform: ping
    interval_seconds: 10
    consider_home: 30
    hosts:
      rpi3: YOUR_HA_IP

sensor:
  - platform: rest
    name: HA time
    resource: http://YOUR_HA_IP:8123/api/states/sensor.date__time?api_password=YOUR_HA_PWD
    value_template: '{{ value_json.state }}'
  - platform: time_date
    display_options:
      - 'date_time'

mysensors:
  gateways:
    - device: '/dev/ttyUSB0'
      persistence_file: '/home/pi/.homeassistant/mysensors.json'
  optimistic: false
  persistence: true
  version: '2.0'

group:
  everything:
    name: Everything
    entities:
      - sensor.date__time
      - sensor.ha_time
      - device_tracker.rpi3
      - switch.backup_gateway_0_1
      - switch.backup_gateway_0_200

the arduino sketch is also simple:

#define MY_DEBUG 
#define MY_RADIO_NRF24
#define MY_RF24_CHANNEL 40 //voor de backup gateway
#define MY_GATEWAY_SERIAL

#include <MySensors.h>  
#define RELAY_1  5  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define RELAY_ON 1  // GPIO value to write to turn on attached relay
#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
#define CHILD_ID_RELAY1 1
MyMessage msg1(CHILD_ID_RELAY1, V_STATUS);

#define CONTROLESWITCH 200
MyMessage msgcontroleswitch(CONTROLESWITCH, V_STATUS);
bool reported=false;
long time=0;
long time2=0;

void before() { 
    pinMode(RELAY_1, OUTPUT);   
    digitalWrite(RELAY_1, loadState(CHILD_ID_RELAY1)?RELAY_ON:RELAY_OFF);
}

void setup() {
}

void presentation() {
  sendSketchInfo("backup gateway", "1.0");  
  present(CONTROLESWITCH,S_BINARY);
  present(CHILD_ID_RELAY1, S_BINARY);
}

void loop() {
  if ( not reported){
    report();
    reported=true;    
  }
  if (millis()-time>1800000){
    reportagain();
  }
}

void receive(const MyMessage &message) {
  if (message.type==V_STATUS) {    
     if (message.sensor==CONTROLESWITCH){
        reportagain();
     }
     if (message.sensor==CHILD_ID_RELAY1){   
        digitalWrite(RELAY_1, message.getBool()?RELAY_ON:RELAY_OFF);
        saveState(CHILD_ID_RELAY1, message.getBool());
        send(msg1.set(message.getBool()));       
     } 
  }
}
void report(){
  send(msgcontroleswitch.set(0));  
  send(msg1.setSensor(CHILD_ID_RELAY1).set(loadState(CHILD_ID_RELAY1)));
}
void reportagain(){
  send(msgcontroleswitch.set(0));  
  send(msg1.setSensor(CHILD_ID_RELAY1).set(loadState(CHILD_ID_RELAY1)));
  time=millis();
}

in HA it shows like this:

then i have an app in Appdaemon:

###########################################################################################
#                                                                                         #
#  Rene Tode ( [email protected] )                                                            #
#  2017/05/17 Germany                                                                     #
#                                                                                         #
###########################################################################################

import appdaemon.appapi as appapi
import datetime
import time

class check_running(appapi.AppDaemon):

  def initialize(self):
    runtime = datetime.time(12, 0, 0)
    self.timestamp = datetime.datetime.now()
    self.run_every(self.Check_if_still_running,self.timestamp,10)        

  def Check_if_still_running(self, kwargs):
    self.timestamp = datetime.datetime.now()
    own_time = datetime.datetime.strptime(self.get_state("sensor.date__time"),"%Y-%m-%d, %H:%M")
    ha_time_str = self.get_state("sensor.ha_time")
    if ha_time_str == "unknown":
      self.log("Warning: Home Assistant is down!")
      self.run_in(self.restartRPI,60)
    else:
      ha_time = datetime.datetime.strptime(ha_time_str,"%Y-%m-%d, %H:%M")
      time_difference = own_time - ha_time
      if time_difference > datetime.timedelta(seconds=60):
        self.log("Warning: There is a difference bigger then 60 seconds")
      else:
        self.log("Home Assistant is running correct")
    if self.get_state("device_tracker.rpi3") != "home":
      self.log("Warning: The RPI sensor shows that the RPI isnt running!")
      self.run_in(self.restartRPI,60)

  def restartRPI(self.kwargs):
    if self.get_state("device_tracker.rpi3") != "home" or self.get_state("sensor.ha_time") == "unknown":
      self.turn_off("switch.backup_gateway_0_1")
      time.sleep(5)
      self.turn_on("switch.backup_gateway_0_1")      
 

and the appdaemon.cfg is quite empty also:

[AppDaemon]
ha_url = http://YOUR_BACKUP_HA_IP:8123
ha_key = YOUR_BACKUP_HA_PWD

logfile = STDOUT
errorfile = /home/pi/.homeassistant/appdasherror.log

app_dir = /home/pi/.homeassistant/apps/

threads = 20
timeout = 30
disable_apps = 0
dash_force_compile = 0
dash_compile_on_start = 1

[checkrunning]
module = check_running
class = check_running

next part of my project is restarting a complete copy from HA in the RPI 2 if the RPI 3 fails completely.
there are a few parts hard to realize because of the fact that the second RPI has another IP address.
could be that i create a third RPI. which is normally not running with the same IP and i let the RPI 2 start that if the running RPI fails completely. settings could be retrieved from the backup server after startup before HA starts.

i hope this helps others to create ideas or make their own emergency switch.

2 Likes