[OBSOLETE] Host Pinger (IP based Online State / Presence)

Thanks for the link. “/sbin/ifdown” instead “ifdown,” I think that might be the problem in my script.

I know wired is better than wireless, but all LAN ports on my router are occupied and I don’t want to buy more hardware. Besides, I need to keep the Pi close to my desktop in case I need to plug in the monitor to see if it’s dead. A wired connection probably won’t help much - I reboot my router at 3am every Sunday. After last night’s reboot I couldn’t ssh to the Pi, so I connected it with a cable, but still nothing happened. I had to power cycled it.

Well if you are interested… I’ve written the Arduino sketch (or at least most of it) which means you could run this on a Wemos D1 Mini…

Still a little work to do… but it’s late here so going to bed… will attempt to finish tomorrow!

1 Like

Okay, got it to work with this line in the crontab:
@reboot sleep 60 && /usr/bin/mono /home/pi/STHostPinger.exe </dev/null >>/home/pi/sthp.log 2>&1

I am using an ASUS router with dual WAN support. My primary ISP is a cable company, and I use a mobile hotspot as the failover WAN. Last month the cable company had an outage, and my kid used almost 4GB cell data streaming Netflix. That’s why I want to use this smart app to prevent surprises.

I just configured the “Notify Me When” smart app, hoping it would notify me when the Host Pinger’s virtual switch for the mobile hotspot turns on, but so far I have only received one notification and it was delayed by 30 minutes.

I have two wish list items:

  1. “HH:mm” for 24-hour display in the log since “tt” is not used for AM/PM.
  2. If SendGetRequest() failed, add the host to a ResendList and resend the status next time regardless whether the host is Active or Inactive. Something like this:

if (pingable)
{
if (!ActiveClients.Contains(element) || ResendList.Contains(element) ) {

Otherwise a status change during an outage might not be reflected after the connection is restored.

Thanks!

04-Mar-18 06:07 [ONLINE] 192.168.100.1
04-Mar-18 06:07 [OFFLINE] 192.168.0.1
04-Mar-18 06:10 [WENT OFFLINE] 192.168.100.1
04-Mar-18 06:10 [ONLINE] 192.168.0.1
04-Mar-18 06:15 [WENT OFFLINE] 192.168.0.1
04-Mar-18 06:16 [ONLINE] 192.168.100.1
04-Mar-18 06:26 [WENT OFFLINE] 192.168.100.1
Failed to SendGetRequest: The request timed out
04-Mar-18 06:27 [ONLINE] 192.168.0.1
Failed to SendGetRequest: Error: ConnectFailure (No route to host)
04-Mar-18 06:33 [ONLINE] 192.168.100.1
04-Mar-18 06:33 [WENT OFFLINE] 192.168.0.1

1 Like

The update to time shouldn’t be an issue, I thought it was 24 hours, will need to see how best to handle the error handling… will need to parse the response or lack of from ST and not update the previous state if not sent… will have a look this evening…

Actually for my use it’s good enough to remove a host from both Active and Inactive lists when there is an error connecting to ST. No Resend list will be necessary. It’s just like restarting the program.

My thinking is not to have a re-send list anyway… it just wont log the state change unless it’s successful… so will continue re-sending the same change until it’s successful, but wont send if the state device comes back online… if you start queuing you could be sending an old state…

To be honest I can’t remember how the code held previous state… but will take a look in a bit when I get home and see what I can do…

@camedia EXE V3 uploaded… time now in 24 hour format and it will retry sending to SmartThings at your specified interval until either successful or until the ping state returns to the last known state in ST.

Thanks for the great work!

I tested it by rebooting the Raspberry Pi, unplugging the cable modem, and reconnecting the cable modem after 10 minutes. I received SMS messages and notifications from ST after the ISP changed.

Here is the log. The initial message still shows V2.0.

—SmartThings Host Pinger V2.0—

Loading config…
Loaded
DebugLevel is set to 1
Running …
05-Mar-18 17:30 [OFFLINE] 192.168.100.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:30 [OFFLINE] 192.168.0.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:31 [ONLINE] 192.168.100.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:31 [OFFLINE] 192.168.0.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:31 [ONLINE] 192.168.100.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:31 [OFFLINE] 192.168.0.1
Failed to send request to SmartThings: Error: NameResolutionFailure [RETRYING…]
05-Mar-18 17:32 [ONLINE] 192.168.100.1
Successfully sent to SmartThings
05-Mar-18 17:32 [OFFLINE] 192.168.0.1
Successfully sent to SmartThings
05-Mar-18 17:40 [WENT OFFLINE] 192.168.100.1
Failed to send request to SmartThings: Error: ConnectFailure (Network is unreachable) [RETRYING…]
05-Mar-18 17:41 [WENT OFFLINE] 192.168.100.1
Failed to send request to SmartThings: The request timed out [RETRYING…]
05-Mar-18 17:43 [ONLINE] 192.168.0.1
Successfully sent to SmartThings
05-Mar-18 17:43 [WENT OFFLINE] 192.168.100.1
Successfully sent to SmartThings
05-Mar-18 17:50 [ONLINE] 192.168.100.1
Successfully sent to SmartThings
05-Mar-18 17:50 [WENT OFFLINE] 192.168.0.1
Successfully sent to SmartThings

So just to double check, all working as expected? Apart from me forgetting to update the version number in the logging? :slight_smile:

Yes, everything is working. Again, thank you!

Yesterday Samsung sent out an email saying the current ST app would be replaced soon. Hopefully everything will continue working after Samsung merged its smart home apps.

1 Like

It’s inevitable loads of stuff in the app will stop working, but shouldn’t impact already installed apps…

NEW - Host Pinger Arduino / ESP8266 EDITION

I’m running this on an Wemos D1 Mini (ESP8266) which is tiny, very low power, connects to Wifi and only costs 2-3 £ or $, connect it to a USB power supply and you have an always on method of pinging devices and sending to SmartThings without running the EXE on a dedicated PC…

I am not running this in a real world scenario so can’t confirm on reliability, as I have only just written the code and I’m not at home right now to leave it going… but if anyone wants to give it a go you will need to add the details in the settings section, load the sketch to your device using the Arduino IDE and it should then start running, output will be to the serial monitor…

By the way this is the first thing I have written for Arduino so any changes or suggestions welcome… feel free to put in a pull request…

Tagging @Jason_Brown as I expect you may be interested…

EDIT: V1.1 now loaded with change so WiFi AP is not broadcast

1 Like

I decided to use bash and wget to write a pinger. Everything in one file.

I’d appreciate it if someone can tell me how to format the code below. The site doesn’t allow me to add more replies to this topic because I am a new user. All I know is to use the HTML pre tag to enclose the entire script.

#!/bin/bash

# SmartThings IDE configuration
IDE="https://graph-na04-useast2.api.smartthings.com"

# SmartThings application ID and access token
# Get them from the Host Pinger SmartApp under Endpoint Setup Details.
ACCESS_TOKEN="paste-your-access-token-here"
APP_ID="paste-your-app-id-here"
ST_ENDPOINT="${IDE}/api/smartapps/installations/${APP_ID}/statechanged"

# Add hosts you want to ping.
declare -A HOST_STATUS_MAP=(
    [192.168.100.1]="unknown"
    [192.168.0.1]="unknown"
)

# ping interval in seconds
PING_INTERVAL=30
FORCE_UPDATE_INTERVAL=86400
LAST_UPDATE="$(date +%s)"

echo `date` "- Starting SmartThings Host Pinger..."
while true
do
    FORCE_UPDATE=false
    CURR_TIME="$(date +%s)"

    if (( CURR_TIME - LAST_UPDATE > FORCE_UPDATE_INTERVAL ))
    then
        FORCE_UPDATE=true
        echo -e "\nForcing SmartThings update..."
    fi

    for IP_ADDR in "${!HOST_STATUS_MAP[@]}"
    do
        HOST_STATUS="unknown"
        WGET_STATUS=0

        if /bin/ping -c 2 ${IP_ADDR} > /dev/null
        then
            HOST_STATUS="online"
        else
            HOST_STATUS="offline"
        fi

        ST_URL="${ST_ENDPOINT}/${HOST_STATUS}?access_token=${ACCESS_TOKEN}&ipadd=${IP_ADDR}"

        # Only update the status if it has changed or forced update is due.

        if [ "${HOST_STATUS}" != "${HOST_STATUS_MAP[${IP_ADDR}]}" ] || ${FORCE_UPDATE}
        then
            /usr/bin/wget -q -O /dev/null -T 10 "${ST_URL}"
            WGET_STATUS=$?
            echo

            # Update the status map if SmartThings was successfully updated.

            if [ ${WGET_STATUS} -eq 0 ]
            then
                HOST_STATUS_MAP[${IP_ADDR}]=${HOST_STATUS}
                LAST_UPDATE="$(date +%s)"
                echo `date` "- ${IP_ADDR} status: [${HOST_STATUS}]"
            else
                echo `date` "- Unable to reach SmartThings Endpoint."
            fi
        fi
    done

#    echo "All hosts: ${!HOST_STATUS_MAP[@]}"
#    echo "All status: ${HOST_STATUS_MAP[@]}"
#    echo "sleeping..."
    sleep ${PING_INTERVAL}
    echo -n "*"
done
4 Likes

@camedia Great work now the 4th way of pinging devices, who thought pinging would be so popular!

I’ll add a link in the 4th post!

By the way you can select and format the code as code if you edit your post (it doesn’t really matter but sometimes makes it easier…

A Bourne shell script that runs on an Asus router with original firmware to detect if a failover has happened. I use it to turn on an orange light behind my TV when the router fails over to the secondary WAN, which is a mobile hotspot.

I am not using ping to detect the change. Instead I use the router’s “nvram get wanX_realip_ip” command. It shows that the Host Pinger smart app can be used as a more general purpose resource state tracker.

The BEGIN_DATA and END_DATA variables should have the values below. They didn’t display correctly when I used the HTML pre tag to enclose the entire script.
BEGIN_DATA=’<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s=“http://schemas.xmlsoap.org/soap/envelope/” s:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”><s:Body><u:SetBinaryState xmlns:u=“urn:Belkin:service:basicevent:1”>‘
END_DATA=’</u:SetBinaryState></s:Body></s:Envelope>’


#!/bin/sh

#### This script runs on a Asus router with original Asus firmware.
#### It checks the status of the primary and secondary/failover WAN
#### and updates the Host Pinger smart app when the status changes.
#### It also turns on a Wemo Mini smart plug on the LAN when a 
#### failover happens.

# SmartThings IDE configuration. Change it to your own IDE URL.
IDE="https://graph-na04-useast2.api.smartthings.com"

# SmartThings application ID and access token
# Get them from the Host Pinger SmartApp under Endpoint Setup Details.
ACCESS_TOKEN="paste-your-access-token-here"
APP_ID="paste-your-app-id-here"
ST_ENDPOINT="${IDE}/api/smartapps/installations/${APP_ID}/statechanged"

# ping interval in seconds
PING_INTERVAL=30
FORCE_UPDATE_INTERVAL=14400
FORCE_UPDATE=false
LAST_UPDATE=$(date +%s)

# Primary WAN
WAN0_IP_ADDR=192.168.100.1
WAN0_PREV_STAT=unknown

# Failover WAN
WAN1_IP_ADDR=192.168.0.1
WAN1_PREV_STAT=unknown

# IP of Wemo Mini smart plug
WEMO_IP=192.168.1.137

H_ACCEPT="Accept: "
H_CONTENT='Content-type: text/xml; charset=utf-8'
H_SOAPACTION="SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\""
BEGIN_DATA=' '
END_DATA=' '

#### Turn on/off Wemo Mini
toggle_wemo()
{
    ON_OFF=$1

    # Wemo's web service port can change periodically.
    # Test which port is currently open.
    for PTEST in 49152 49153 49154 49155
    do
        PORTTEST=$(wget -q -t 1 -T 5 -O - http://$WEMO_IP:$PTEST/setup.xml | grep "root")                      
        if [ "$PORTTEST" != "" ]; then
            PORT=$PTEST
            break
        fi
    done    
    
#    echo "${BEGIN_DATA}${ON_OFF}${END_DATA}" 

    if [ "${PORT}" != "" ]; then
        wget --header="${H_ACCEPT}" \
             --header="${H_CONTENT}" \
             --header="${H_SOAPACTION}" \
             --post-data="${BEGIN_DATA}${ON_OFF}${END_DATA}" \
             -q -t 1 -T 10 -o /dev/null -O /dev/null http://$WEMO_IP:$PORT/upnp/control/basicevent1
    fi
}

#### Update device status in SmartThings
update_st_status()
{
    WAN_NUM=$1
    IP_ADDR=$2
    HOST_STATUS=$3
    
    ST_URL="${ST_ENDPOINT}/${HOST_STATUS}?access_token=${ACCESS_TOKEN}&ipadd=${IP_ADDR}"
    
    wget -q -t 1 -T 10 -o /dev/null -O /dev/null --no-check-certificate "${ST_URL}"
    WGET_STATUS=$?
    
    if [ ${WGET_STATUS} -eq 0 ]; then
        LAST_UPDATE=$(date +%s)
        eval "WAN${WAN_NUM}_PREV_STAT=${HOST_STATUS}"
    fi
}

#### Use Asus router's "nvram get wanX_realip_ip" command
#### to check if a WAN is active. X is 0 for the primary WAN
#### and 1 for the secondary/failover WAN.
check_wan_status()
{
    WAN_NUM=$1
    IP_ADDR=$2
    PREV_STATUS=$3
    
    WAN_IP_ADDR=$(nvram get wan${WAN_NUM}_realip_ip)
    
    if [ "${WAN_IP_ADDR}" != "" ]; then
        WAN_STATUS=online
    else
        WAN_STATUS=offline
    fi

    if [ "${WAN_STATUS}" != "${PREV_STATUS}" ] || ${FORCE_UPDATE} ; then
        # Update the Wemo plug directly instead of waiting for SmartThings
        # because during failover the ST hub might take minutes to
        # reconnect to the cloud.
        if [ ${WAN_NUM} -eq 1 ] && [ ${WAN_STATUS} = "online" ]; then
           toggle_wemo 1
        elif [ ${WAN_NUM} -eq 1 ] && [ ${WAN_STATUS} = "offline" ]; then
           toggle_wemo 0
        fi
        
        echo $(date) ${WAN_NUM} "${IP_ADDR}" "${WAN_STATUS}"
        update_st_status ${WAN_NUM} "${IP_ADDR}" "${WAN_STATUS}"
    fi
}

#### Main
echo $(date) "Starting SmartThings Pinger..."

while true
do
    CURR_TIME=$(date +%s)
    TIME_LAPSED=$(expr ${CURR_TIME} - ${LAST_UPDATE} )
#    echo "Time lapsed: ${TIME_LAPSED}"
    
    if [ ${TIME_LAPSED} -ge ${FORCE_UPDATE_INTERVAL} ]; then
        FORCE_UPDATE=true
        echo $(date) "Forced update: $FORCE_UPDATE"
    else
        FORCE_UPDATE=false
    fi

    check_wan_status 0 ${WAN0_IP_ADDR} ${WAN0_PREV_STAT}
    check_wan_status 1 ${WAN1_IP_ADDR} ${WAN1_PREV_STAT}
    
    sleep ${PING_INTERVAL}
done


2 Likes

Something strange here, I wanted to transfer my sthostpinger to another rpi device so I copied the exact sthostpinger and config file to another rpi. However now when I run I got the following

Failed to SendGetRequest: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure

Any settings I need to reset or reconfigure when changing to another rpi?

It’s an issue with your mono install…and the security certificates

@rontalley worked this out in post 34, although the link in his post doesn’t go the the right place anymore… so here’s a fresh link :slight_smile:

http://www.mono-project.com/download/stable/#download-lin-raspbian

2 Likes

Updated my post as well.

I still refer people to this all the time! Been working for me since day 1.

Thanks again for this awesome app!

1 Like

I also updated the first post under the troubleshooting section yesterday too.

Thanks for your awesome help and ideas :slight_smile:

1 Like

Ahh thanks, that was the issue. Maybe include it in the installation notes.