Here's the solution I came up with:
- It runs on a pi zero W, powered by the router's USB port.
- Wireless IP is set to dynamic
- I have MQTT (Mosquitto) running on an always on pi that is accessible through my firewall
- Every five minutes it checks the WAN IP. If it's new, it appends a row to an sqlite3 DB and MQTT publishes the IP & Time to topic IP/HOSTNAME on the MQTT server. It also writes the IP & human readable date to a 4 line LCD
- I haven't yet tried getting an MQTT client on my Android phone, subscribing to the topic IP/HOSTNAME, but it works when I run a client on my desktop/laptop.
[{"id":"aa0f87db.45865","type":"inject","z":"1853a7d0.062638","name":"","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["e755640a.6d17d","f1ffdff2.696fc"]]},{"id":"365ac05a.a28b3","type":"http request","z":"1853a7d0.062638","name":"Get current WAN IP","method":"GET","ret":"txt","url":"http://api.ipify.org/","tls":"","x":330,"y":220,"wires":[["29f04258.fed3e6","9ae8d51c.424a8"]]},{"id":"9ae8d51c.424a8","type":"function","z":"1853a7d0.062638","name":"Store IP in flow variable","func":"//store IP in a flow scoped variable\nvar flowIP;\n\nflow.set(\"flowIP\", msg.payload);\n\nmsg.ip = msg.payload;\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":280,"wires":[["dec2afa5.4d27","f2bac181.2a91d","395f8fcf.dbabd8"]]},{"id":"e755640a.6d17d","type":"function","z":"1853a7d0.062638","name":"Get current time","func":"//store now-time in a flow scoped variable\nvar justSeen, justSeenUTC;\n\nflow.set(\"now_time\", msg.payload);\n\nmsg.justSeen = msg.payload;\n\n//return msg;\n\n\n\n var date = new Date(msg.justSeen);\n msg.justSeenUTC = date.toUTCString();\n return msg;\n\n\n\n//msg.justSeen = msg.payload;\n//return msg;","outputs":1,"noerr":0,"x":320,"y":180,"wires":[["a6539c2.5b98b6","365ac05a.a28b3"]]},{"id":"dec2afa5.4d27","type":"debug","z":"1853a7d0.062638","name":"IP","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":570,"y":240,"wires":[]},{"id":"f1ffdff2.696fc","type":"sqlite","z":"1853a7d0.062638","mydb":"7331442a.3f9e44","sqlquery":"fixed","sql":"CREATE TABLE IF NOT EXISTS IPlog (\n ID INTEGER PRIMARY KEY AUTOINCREMENT,\n currentIP TEXT,\n lastSeen TEXT,\n firstSeen TEXT\n);\n","name":"IPmonLog","x":310,"y":120,"wires":[["a77a977d.13d05"]],"outputLabels":["Table created?"]},{"id":"de03078c.76de5","type":"sqlite","z":"1853a7d0.062638","mydb":"7331442a.3f9e44","sqlquery":"msg.topic","sql":"\n\n","name":"Insert or update IP","x":770,"y":440,"wires":[["3ad79da0.9e81aa","d52074d4.e6d8d8"]]},{"id":"f2bac181.2a91d","type":"sqlite","z":"1853a7d0.062638","mydb":"7331442a.3f9e44","sqlquery":"fixed","sql":"SELECT currentIP as lastRecordedIP from IPlog WHERE ID = (SELECT MAX(ID) FROM IPlog);","name":"Query content","x":320,"y":340,"wires":[["5b9458c5.c189b","a46b38d0.9c6e9"]]},{"id":"5b9458c5.c189b","type":"debug","z":"1853a7d0.062638","name":"Get last row currentIP","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":620,"y":340,"wires":[]},{"id":"29f04258.fed3e6","type":"debug","z":"1853a7d0.062638","name":"WAN-IP","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":740,"y":220,"wires":[]},{"id":"a77a977d.13d05","type":"debug","z":"1853a7d0.062638","name":"Create table","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":590,"y":120,"wires":[]},{"id":"a46b38d0.9c6e9","type":"function","z":"1853a7d0.062638","name":"SQL to Insert or update a row","func":"var lastRecordedIP = \"0.0.0.0\";\nif (msg.payload.length !== 0) { \n lastRecordedIP = msg.payload[0].lastRecordedIP;\n}\n//msg.ip = msg.payload[0].lastRecordedIP;\nif (lastRecordedIP == msg.ip) {\n msg.topic = \"UPDATE IPlog \" +\n \"SET lastSeen = \" + msg.justSeen +\n \" WHERE ID = (SELECT MAX(ID) FROM IPlog);\"\n return [msg,null];\n} else {\n msg.topic = \"INSERT INTO IPlog (currentIP, firstSeen, lastSeen) \" + \n \"VALUES ('\" + msg.ip + \"','\" + msg.justSeen + \"','\" + msg.justSeen + \"') ;\";\n mqttMessage = \"'\" + msg.ip + \"','\" + msg.justSeen + \"','\" + msg.justSeen + \"'\";\n msg.payload = { mqttMessage };\n return [msg, {payload:msg.justSeen}];\n //return [msg, msg];\n}\nreturn null;\n//return [msg, {payload:msg.justSeen}];","outputs":2,"noerr":0,"x":370,"y":400,"wires":[["de03078c.76de5","2a091618.2c6caa"],["48de3bfc.cac084","58c28bf2.4ac2cc"]]},{"id":"3ad79da0.9e81aa","type":"sqlite","z":"1853a7d0.062638","mydb":"7331442a.3f9e44","sqlquery":"fixed","sql":"SELECT * from IPlog ;","name":"Query ALL content","x":770,"y":500,"wires":[["1d19f479.5960e4"]]},{"id":"1d19f479.5960e4","type":"debug","z":"1853a7d0.062638","name":"Get last row currentIP","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1000,"y":500,"wires":[]},{"id":"d52074d4.e6d8d8","type":"debug","z":"1853a7d0.062638","name":"SQL summary","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":980,"y":440,"wires":[]},{"id":"e4a6e881.f197e8","type":"comment","z":"1853a7d0.062638","name":"SQLite IPmonLog schema","info":"CREATE TABLE IPlog (\nID INTEGER PRIMARY KEY AUTOINCREMENT,\ncurrentIP TEXT,\nlastSeen TEXT,\nfirstSeen TEXT\n);","x":350,"y":60,"wires":[]},{"id":"69785ef5.bf0e5","type":"mqtt out","z":"1853a7d0.062638","name":"Publish new IP","topic":"IP/INDIAN","qos":"0","retain":"false","broker":"9a1f4bd8.e4b938","x":320,"y":580,"wires":[]},{"id":"48de3bfc.cac084","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":570,"y":460,"wires":[]},{"id":"a6539c2.5b98b6","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":570,"y":180,"wires":[]},{"id":"a9f70c33.1d0f58","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":570,"y":540,"wires":[]},{"id":"7d60391b.c4eba","type":"function","z":"1853a7d0.062638","name":"Date object to string","func":"msg.justSeenString = msg.justSeen.toISOString();\n/* msg.justSeen.years.toString() + \"-\" +\n msg.justSeen.months.toString() + \"-\" + \n msg.justSeen.date.toString() + \"_\" + \n msg.justSeen.hours.toString() + \":\" + \n msg.justSeen.minutes.toString() + \":\" +\n msg.justSeen.seconds.toString() + \".\" + \n msg.justSeen.milliseconds.toString();\n*/ \nreturn msg;","outputs":1,"noerr":0,"x":620,"y":60,"wires":[[]]},{"id":"bac5770e.fd6a08","type":"function","z":"1853a7d0.062638","name":"Add IP to report string","func":"msg.mqttString = flow.get(\"flowIP\") + \" - First detected: \" + msg.payload;\nmsg.payload = msg.mqttString;\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":520,"wires":[["a9f70c33.1d0f58","69785ef5.bf0e5"]]},{"id":"2a091618.2c6caa","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":730,"y":380,"wires":[]},{"id":"824480a1.3ec35","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":570,"y":500,"wires":[]},{"id":"58c28bf2.4ac2cc","type":"moment","z":"1853a7d0.062638","name":"","topic":"","input":"payload","format":"YYYY-MM-DD HH:mm:ss","locale":"en_GB","output":"payload","x":340,"y":460,"wires":[["bac5770e.fd6a08","824480a1.3ec35"]]},{"id":"395f8fcf.dbabd8","type":"moment","z":"1853a7d0.062638","name":"","topic":"","input":"justSeenUTC","format":"DD/MM/YY HH:mm","locale":"en_GB","output":"payload","fakeUTC":false,"x":620,"y":280,"wires":[["fd1fd35b.f9f02","d5b042ad.00327"]]},{"id":"cf2067f8.51fc78","type":"rpi-lcd","z":"1853a7d0.062638","name":"Current IP -line 1","pins":"37,35,33,31,29,22","x":1090,"y":280,"wires":[]},{"id":"fd1fd35b.f9f02","type":"function","z":"1853a7d0.062638","name":"Display IP","func":"var displayString;\ndisplayString2 = flow.get(\"flowIP\");\nmsg.payload = displayString2;\nreturn msg;","outputs":1,"noerr":0,"x":840,"y":280,"wires":[["cf2067f8.51fc78","2878b2bf.68c4c6"]]},{"id":"2878b2bf.68c4c6","type":"debug","z":"1853a7d0.062638","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1070,"y":240,"wires":[]},{"id":"d5b042ad.00327","type":"function","z":"1853a7d0.062638","name":"Display just seen","func":"msg.payload = \"2:\" + msg.payload\nreturn msg;","outputs":1,"noerr":0,"x":870,"y":320,"wires":[["cf2067f8.51fc78"]]},{"id":"7331442a.3f9e44","type":"sqlitedb","z":"","db":"/home/pi/IPmonLog.db","mode":"RWC"},{"id":"9a1f4bd8.e4b938","type":"mqtt-broker","z":"","name":"DriveCam","broker":"212.159.76.88","port":"11883","tls":"","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
You'd need to:
Install SQLite3 and create a DB, called IPmonLog.db
Install the Node-Red SQLite node in the palette
If you want the LCD, install the Node-Red GPIO and LCD nodes into the palette
I have a monitor that subscribes to my Mum's WAN IP and another subscription for my in-law's WAN IP
The audio is optional, obvs.
[{"id":"e49d3f3a.bfccb8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"cd19e722.e299a8","type":"mqtt in","z":"e49d3f3a.bfccb8","name":"","topic":"IP/INDIAN","qos":"2","broker":"90ae653c.70b1b8","x":320,"y":100,"wires":[["16c1e9e0.49959e","221658d3.fe90f8","98c1607.04bed2"]]},{"id":"16c1e9e0.49959e","type":"debug","z":"e49d3f3a.bfccb8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":100,"wires":[]},{"id":"28c3e2dc.6464fe","type":"mqtt in","z":"e49d3f3a.bfccb8","name":"","topic":"IP/ARCTIC","qos":"2","broker":"90ae653c.70b1b8","x":320,"y":240,"wires":[["eceb9588.c4a96","71995d30.3dcf7c","f0f1bcf4.43db88"]]},{"id":"eceb9588.c4a96","type":"debug","z":"e49d3f3a.bfccb8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":240,"wires":[]},{"id":"221658d3.fe90f8","type":"play audio","z":"e49d3f3a.bfccb8","name":"","voice":"0","x":550,"y":140,"wires":[]},{"id":"71995d30.3dcf7c","type":"play audio","z":"e49d3f3a.bfccb8","name":"","voice":"1","x":550,"y":280,"wires":[]},{"id":"98c1607.04bed2","type":"function","z":"e49d3f3a.bfccb8","name":"e-mail INDIAN ","func":"msg.payload = \"INDIAN - \" + msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":560,"y":180,"wires":[["e602b4e9.5026e8"]]},{"id":"f0f1bcf4.43db88","type":"function","z":"e49d3f3a.bfccb8","name":"e-mail ARCTIC ","func":"msg.payload = \"ARCTIC - \" + msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":560,"y":320,"wires":[["e602b4e9.5026e8"]]},{"id":"e602b4e9.5026e8","type":"e-mail","z":"e49d3f3a.bfccb8","server":"smtp.gmail.com","port":"465","secure":true,"name":"","dname":"e-mail to me","x":850,"y":240,"wires":[]},{"id":"90ae653c.70b1b8","type":"mqtt-broker","z":"","name":"DriveCam","broker":"212.159.76.88","port":"11883","tls":"","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]