[{"data":1,"prerenderedAt":1032},["ShallowReactive",2],{"Categories":3,"NavIndexCategoriesCountFooter":203,"content-\u002F2020\u002F01\u002F29\u002Fmonitoring-ip-changes\u002F":204},[4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,68,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202],{"category":5},"System Administration",{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},"Software Development",{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":27},{"category":27},{"category":67},"Drones & RC",{"category":69},"DIY Projects",{"category":67},{"category":72},"Photography",{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":67},{"category":67},{"category":72},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":5},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":67},{"category":67},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":72},{"category":67},{"category":138},"3D Printing - Laser Cutting - CNC",{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":5},{"category":138},{"category":27},{"category":27},{"category":138},{"category":138},{"category":72},{"category":158},"Photography,3D Printing - Laser Cutting - CNC",{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":67},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":181},"Home Assistant",{"category":181},{"category":72},{"category":27},{"category":27},{"category":72},{"category":138},{"category":5},{"category":72},{"category":72},{"category":138},{"category":27},{"category":181},{"category":181},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},191,{"id":205,"title":206,"body":207,"category":5,"date":1018,"description":213,"embedImage":1019,"extension":1020,"image":998,"intro":1021,"meta":1022,"navigation":486,"path":1023,"seo":1024,"series":1019,"sitemap":1025,"stem":1026,"tags":1027,"__hash__":1031},"content\u002F2020\u002F01\u002F29\u002Fmonitoring-ip-changes.md","Monitoring IP changes",{"type":208,"value":209,"toc":1009},"minimark",[210,214,217,222,235,275,282,285,348,352,355,366,398,403,413,416,547,561,569,572,578,616,622,625,628,841,844,847,850,853,857,865,868,879,882,904,907,911,914,963,974,978,981,989,992,999,1002,1005],[211,212,213],"p",{},"My ISP connection to home does not offer a static IP address. It does maintain a stable IP address - but after a period offline (either a fault or a power failure or similar) then that IP address may change.",[211,215,216],{},"Up until recently - I had a cron job that checked for changes and mailed me if it had changed. But - after moving my site DNS to cloudflare - I could now switch to automatically updating the domain.",[218,219,221],"h2",{"id":220},"ipifyorg","ipify.org",[211,223,224,225],{},"First let's get the current IP using ",[226,227,234],"a",{"href":228,"rel":229,"target":233},"https:\u002F\u002Fipify.org",[230,231,232],"nofollow","noopener","noreferer","_blank","ipify",[236,237,242],"pre",{"className":238,"code":239,"language":240,"meta":241,"style":241},"language-shell shiki shiki-themes github-dark","IP=`curl https:\u002F\u002Fapi.ipify.org 2> \u002Fdev\u002Fnull`\n","shell","",[243,244,245],"code",{"__ignoreMap":241},[246,247,250,254,258,262,266,269,272],"span",{"class":248,"line":249},"line",1,[246,251,253],{"class":252},"s95oV","IP",[246,255,257],{"class":256},"snl16","=",[246,259,261],{"class":260},"sU2Wk","`",[246,263,265],{"class":264},"svObZ","curl",[246,267,268],{"class":260}," https:\u002F\u002Fapi.ipify.org ",[246,270,271],{"class":256},"2>",[246,273,274],{"class":260}," \u002Fdev\u002Fnull`\n",[211,276,277,278,281],{},"So - at this point - you hopefully have a ",[243,279,280],{},"$IP"," with your current external IP address. But - you may have an error message. So - we need to check that.",[211,283,284],{},"I just googled \"bash IP regex\" and ended up with the following:",[236,286,288],{"className":238,"code":287,"language":240,"meta":241,"style":241},"if [[ $IP =~ ^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$ ]]; then\n    # Check to see if IP has changed\nelse\n    # Could not fetch IP from ipify\nfi\n",[243,289,290,323,330,336,342],{"__ignoreMap":241},[246,291,292,295,298,301,304,308,311,313,315,317,320],{"class":248,"line":249},[246,293,294],{"class":256},"if",[246,296,297],{"class":252}," [[ $IP ",[246,299,300],{"class":256},"=~",[246,302,303],{"class":252}," ^[0-9]{1,3}",[246,305,307],{"class":306},"sDLfK","\\.",[246,309,310],{"class":252},"[0-9]{1,3}",[246,312,307],{"class":306},[246,314,310],{"class":252},[246,316,307],{"class":306},[246,318,319],{"class":252},"[0-9]{1,3}$ ]]; ",[246,321,322],{"class":256},"then\n",[246,324,326],{"class":248,"line":325},2,[246,327,329],{"class":328},"sAwPA","    # Check to see if IP has changed\n",[246,331,333],{"class":248,"line":332},3,[246,334,335],{"class":256},"else\n",[246,337,339],{"class":248,"line":338},4,[246,340,341],{"class":328},"    # Could not fetch IP from ipify\n",[246,343,345],{"class":248,"line":344},5,[246,346,347],{"class":256},"fi\n",[218,349,351],{"id":350},"cloudflare","Cloudflare",[211,353,354],{},"So - lets think about the IP address received branch. Next thing we need to do is to get the current value.",[211,356,357,358,361,362,365],{},"My site's main nameserver is ben.ns.cloudflare.com - so we'll use dig to query that. Normal dig output contains a fair amount of information. We'll use the ",[243,359,360],{},"noall"," option to disable all parts - then explicitly turn on just the ",[243,363,364],{},"answer"," option to get just that line. Finally we pipe the rest through sed to strip off all information up to the A record itself:",[236,367,369],{"className":238,"code":368,"language":240,"meta":241,"style":241}," DNS=`dig @BEN.NS.CLOUDFLARE.COM +noall +answer [YOUR_DOMAIN] | sed -e \"s\u002F.*A[[:blank:]]*\u002F\u002F\"`\n",[243,370,371],{"__ignoreMap":241},[246,372,373,376,378,380,383,386,389,392,395],{"class":248,"line":249},[246,374,375],{"class":252}," DNS",[246,377,257],{"class":256},[246,379,261],{"class":260},[246,381,382],{"class":264},"dig",[246,384,385],{"class":260}," @BEN.NS.CLOUDFLARE.COM +noall +answer [YOUR_DOMAIN] ",[246,387,388],{"class":256},"|",[246,390,391],{"class":264}," sed",[246,393,394],{"class":306}," -e",[246,396,397],{"class":260}," \"s\u002F.*A[[:blank:]]*\u002F\u002F\"`\n",[399,400,402],"h3",{"id":401},"api","API",[211,404,405,406,408,409,412],{},"We can now compare the ",[243,407,280],{}," and ",[243,410,411],{},"$DNS"," values - if they have changed - then we need to update the cloudflare DNS via their API.",[211,414,415],{},"The echo lines (since this is run as a cron script) also trigger an email with the info.",[236,417,419],{"className":238,"code":418,"language":240,"meta":241,"style":241},"    if [[ \"$DNS\" != \"$IP\" ]]; then\n        echo \"IP has changed\"\n        echo \"Current public: $DNS\"\n        echo \"Current active: $IP\"\n\n         curl -X PUT \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F[ZONE_ID]\u002Fdns_records\u002F[HOST_ID]\" \\\n           -H \"Authorization: Bearer [TOKEN]\" \\\n           -H \"Content-Type: application\u002Fjson\" \\\n           --data '{\"type\":\"A\",\"name\":\"[YOUR_DOMAIN]\",\"content\":\"'$IP'\"}'\n      fi\n",[243,420,421,451,459,471,482,488,506,517,527,541],{"__ignoreMap":241},[246,422,423,426,429,432,434,436,439,442,444,446,449],{"class":248,"line":249},[246,424,425],{"class":256},"    if",[246,427,428],{"class":252}," [[ ",[246,430,431],{"class":260},"\"",[246,433,411],{"class":252},[246,435,431],{"class":260},[246,437,438],{"class":256}," !=",[246,440,441],{"class":260}," \"",[246,443,280],{"class":252},[246,445,431],{"class":260},[246,447,448],{"class":252}," ]]; ",[246,450,322],{"class":256},[246,452,453,456],{"class":248,"line":325},[246,454,455],{"class":306},"        echo",[246,457,458],{"class":260}," \"IP has changed\"\n",[246,460,461,463,466,468],{"class":248,"line":332},[246,462,455],{"class":306},[246,464,465],{"class":260}," \"Current public: ",[246,467,411],{"class":252},[246,469,470],{"class":260},"\"\n",[246,472,473,475,478,480],{"class":248,"line":338},[246,474,455],{"class":306},[246,476,477],{"class":260}," \"Current active: ",[246,479,280],{"class":252},[246,481,470],{"class":260},[246,483,484],{"class":248,"line":344},[246,485,487],{"emptyLinePlaceholder":486},true,"\n",[246,489,491,494,497,500,503],{"class":248,"line":490},6,[246,492,493],{"class":264},"         curl",[246,495,496],{"class":306}," -X",[246,498,499],{"class":260}," PUT",[246,501,502],{"class":260}," \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F[ZONE_ID]\u002Fdns_records\u002F[HOST_ID]\"",[246,504,505],{"class":306}," \\\n",[246,507,509,512,515],{"class":248,"line":508},7,[246,510,511],{"class":306},"           -H",[246,513,514],{"class":260}," \"Authorization: Bearer [TOKEN]\"",[246,516,505],{"class":306},[246,518,520,522,525],{"class":248,"line":519},8,[246,521,511],{"class":306},[246,523,524],{"class":260}," \"Content-Type: application\u002Fjson\"",[246,526,505],{"class":306},[246,528,530,533,536,538],{"class":248,"line":529},9,[246,531,532],{"class":306},"           --data",[246,534,535],{"class":260}," '{\"type\":\"A\",\"name\":\"[YOUR_DOMAIN]\",\"content\":\"'",[246,537,280],{"class":252},[246,539,540],{"class":260},"'\"}'\n",[246,542,544],{"class":248,"line":543},10,[246,545,546],{"class":256},"      fi\n",[211,548,549,550,553,554,408,557,560],{},"So - where do you get the ",[243,551,552],{},"ZONE_ID",", ",[243,555,556],{},"HOST_ID",[243,558,559],{},"TOKEN","?",[211,562,563,564,568],{},"Head to ",[226,565,566],{"href":566,"rel":567,"target":233},"https:\u002F\u002Fdash.cloudflare.com\u002F",[230,231,232]," - and when logged in - choose your domain. On the overview page - down and to the right there should be an API section with the Zone ID.",[211,570,571],{},"Next to that should be a link to get a token.",[211,573,574,575,577],{},"You will need to create at least a token for the \"Zone.DNS\" permission on the zone you wish to update. I also added one for \"Zone.Zone\" - since I used the API to get various information - in fact we'll use it to get the ",[243,576,556],{}," using curl to fetch and jq to format:",[236,579,581],{"className":238,"code":580,"language":240,"meta":241,"style":241},"curl \u002Fclient\u002Fv4\u002Fzones\u002F[ZONE_ID]\u002Fdns_records \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -H \"Authorization: Bearer [TOKEN]\" | jq .\n",[243,582,583,592,601],{"__ignoreMap":241},[246,584,585,587,590],{"class":248,"line":249},[246,586,265],{"class":264},[246,588,589],{"class":260}," \u002Fclient\u002Fv4\u002Fzones\u002F[ZONE_ID]\u002Fdns_records",[246,591,505],{"class":306},[246,593,594,597,599],{"class":248,"line":325},[246,595,596],{"class":306},"  -H",[246,598,524],{"class":260},[246,600,505],{"class":306},[246,602,603,605,607,610,613],{"class":248,"line":332},[246,604,596],{"class":306},[246,606,514],{"class":260},[246,608,609],{"class":256}," |",[246,611,612],{"class":264}," jq",[246,614,615],{"class":260}," .\n",[211,617,618,619,621],{},"Scroll through the results until you find the json block for the domain you want to manage - and grab it's \"id\" value - this is the ",[243,620,556],{},".",[623,624],"hr",{},[211,626,627],{},"With an echo in the else branch we end up with the following script which can be run via cron (I run mine once a minute):",[236,629,631],{"className":238,"code":630,"language":240,"meta":241,"style":241},"#!\u002Fbin\u002Fbash\n\nIP=`curl https:\u002F\u002Fapi.ipify.org 2> \u002Fdev\u002Fnull`\n\nif [[ $IP =~ ^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$ ]]; then\n    DNS=`dig @BEN.NS.CLOUDFLARE.COM +noall +answer [YOUR_DOMAIN] | sed -e \"s\u002F.*A[[:blank:]]*\u002F\u002F\"`\n\n    if [[ \"$DNS\" != \"$IP\" ]]; then\n        echo \"IP has changed\"\n        echo \"Current public: $DNS\"\n        echo \"Current active: $IP\"\n\n        curl -X PUT \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F[ZONE_ID]\u002Fdns_records\u002F[HOST_ID]\" \\\n          -H \"Authorization: Bearer [TOKEN]\" \\\n          -H \"Content-Type: application\u002Fjson\" \\\n          --data '{\"type\":\"A\",\"name\":\"[YOUR_DOMAIN]\",\"content\":\"'$IP'\"}'\n    fi\nelse\n    echo \"Current IP Failed - return was: $IP\"\nfi\n",[243,632,633,638,642,658,662,686,707,711,735,741,751,762,767,781,791,800,812,818,823,836],{"__ignoreMap":241},[246,634,635],{"class":248,"line":249},[246,636,637],{"class":328},"#!\u002Fbin\u002Fbash\n",[246,639,640],{"class":248,"line":325},[246,641,487],{"emptyLinePlaceholder":486},[246,643,644,646,648,650,652,654,656],{"class":248,"line":332},[246,645,253],{"class":252},[246,647,257],{"class":256},[246,649,261],{"class":260},[246,651,265],{"class":264},[246,653,268],{"class":260},[246,655,271],{"class":256},[246,657,274],{"class":260},[246,659,660],{"class":248,"line":338},[246,661,487],{"emptyLinePlaceholder":486},[246,663,664,666,668,670,672,674,676,678,680,682,684],{"class":248,"line":344},[246,665,294],{"class":256},[246,667,297],{"class":252},[246,669,300],{"class":256},[246,671,303],{"class":252},[246,673,307],{"class":306},[246,675,310],{"class":252},[246,677,307],{"class":306},[246,679,310],{"class":252},[246,681,307],{"class":306},[246,683,319],{"class":252},[246,685,322],{"class":256},[246,687,688,691,693,695,697,699,701,703,705],{"class":248,"line":490},[246,689,690],{"class":252},"    DNS",[246,692,257],{"class":256},[246,694,261],{"class":260},[246,696,382],{"class":264},[246,698,385],{"class":260},[246,700,388],{"class":256},[246,702,391],{"class":264},[246,704,394],{"class":306},[246,706,397],{"class":260},[246,708,709],{"class":248,"line":508},[246,710,487],{"emptyLinePlaceholder":486},[246,712,713,715,717,719,721,723,725,727,729,731,733],{"class":248,"line":519},[246,714,425],{"class":256},[246,716,428],{"class":252},[246,718,431],{"class":260},[246,720,411],{"class":252},[246,722,431],{"class":260},[246,724,438],{"class":256},[246,726,441],{"class":260},[246,728,280],{"class":252},[246,730,431],{"class":260},[246,732,448],{"class":252},[246,734,322],{"class":256},[246,736,737,739],{"class":248,"line":529},[246,738,455],{"class":306},[246,740,458],{"class":260},[246,742,743,745,747,749],{"class":248,"line":543},[246,744,455],{"class":306},[246,746,465],{"class":260},[246,748,411],{"class":252},[246,750,470],{"class":260},[246,752,754,756,758,760],{"class":248,"line":753},11,[246,755,455],{"class":306},[246,757,477],{"class":260},[246,759,280],{"class":252},[246,761,470],{"class":260},[246,763,765],{"class":248,"line":764},12,[246,766,487],{"emptyLinePlaceholder":486},[246,768,770,773,775,777,779],{"class":248,"line":769},13,[246,771,772],{"class":264},"        curl",[246,774,496],{"class":306},[246,776,499],{"class":260},[246,778,502],{"class":260},[246,780,505],{"class":306},[246,782,784,787,789],{"class":248,"line":783},14,[246,785,786],{"class":306},"          -H",[246,788,514],{"class":260},[246,790,505],{"class":306},[246,792,794,796,798],{"class":248,"line":793},15,[246,795,786],{"class":306},[246,797,524],{"class":260},[246,799,505],{"class":306},[246,801,803,806,808,810],{"class":248,"line":802},16,[246,804,805],{"class":306},"          --data",[246,807,535],{"class":260},[246,809,280],{"class":252},[246,811,540],{"class":260},[246,813,815],{"class":248,"line":814},17,[246,816,817],{"class":256},"    fi\n",[246,819,821],{"class":248,"line":820},18,[246,822,335],{"class":256},[246,824,826,829,832,834],{"class":248,"line":825},19,[246,827,828],{"class":306},"    echo",[246,830,831],{"class":260}," \"Current IP Failed - return was: ",[246,833,280],{"class":252},[246,835,470],{"class":260},[246,837,839],{"class":248,"line":838},20,[246,840,347],{"class":256},[211,842,843],{},"So - what sort of emails does this generate?",[211,845,846],{},"Well - extremely rarely - an \"IP has changed\".",[211,848,849],{},"A little more often - I get a \"Current IP Failed\". In all cases the content is an HTML error page from herokucdn (where the ipify api is hosted).",[211,851,852],{},"This is still not a very frequent occurrance - I run this call once a minute and I may get a few fails a day or none. But - just for fun - let's track it.",[218,854,856],{"id":855},"mqtt","MQTT",[211,858,859,860,621],{},"I already have a raspberry pi running node-red, mqtt (mosquitto), influx DB and grafana - based on this youtube video by Andreas Spiess ",[226,861,864],{"href":862,"rel":863,"target":233},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=JdV4x925au0",[230,231,232],"#255 Node-Red, InfluxDB, and Grafana Tutorial on a Raspberry Pi",[211,866,867],{},"So - if I can post a message when an error occurs - then we can use the same setup to get the information into a graph in grafana.",[211,869,870,871,874,875,878],{},"First - we need ",[243,872,873],{},"mosquitto_pub"," - that's in the ",[243,876,877],{},"mosquitto_clients"," package (at least for me on my debian box).",[211,880,881],{},"Then we simply need to add a call to the else clause. The user and password were created during the setup of the raspberry pi - see the video linked above.",[236,883,885],{"className":238,"code":884,"language":240,"meta":241,"style":241},"\u002Fusr\u002Fbin\u002Fmosquitto_pub -h [PI_HOST] -p 1883 -u [PI_MQTT_USER] -P [PI_MQTT_PASSWORD] -m \"IP Error\" -t cron\u002Fip\n",[243,886,887],{"__ignoreMap":241},[246,888,889,892,895,898,901],{"class":248,"line":249},[246,890,891],{"class":264},"\u002Fusr\u002Fbin\u002Fmosquitto_pub",[246,893,894],{"class":306}," -h",[246,896,897],{"class":252}," [PI_HOST] -p 1883 -u [PI_MQTT_USER] -P [PI_MQTT_PASSWORD] -m ",[246,899,900],{"class":260},"\"IP Error\"",[246,902,903],{"class":252}," -t cron\u002Fip\n",[211,905,906],{},"This then posts a hit to MQTT each time an error occurs calling ipify.",[218,908,910],{"id":909},"node-red","Node-RED",[211,912,913],{},"In Node-RED I created a new flow - it subscribes to the MQTT cron\u002Fip topic. Each message there will be a plain text \"IP Error\" - so it gets passed to a function node that simply wraps it in json:",[236,915,919],{"className":916,"code":917,"language":918,"meta":241,"style":241},"language-javascript shiki shiki-themes github-dark","msg = {\n  payload: {\n    reason: msg.payload,\n  },\n};\n\nreturn msg;\n","javascript",[243,920,921,931,936,941,946,951,955],{"__ignoreMap":241},[246,922,923,926,928],{"class":248,"line":249},[246,924,925],{"class":252},"msg ",[246,927,257],{"class":256},[246,929,930],{"class":252}," {\n",[246,932,933],{"class":248,"line":325},[246,934,935],{"class":252},"  payload: {\n",[246,937,938],{"class":248,"line":332},[246,939,940],{"class":252},"    reason: msg.payload,\n",[246,942,943],{"class":248,"line":338},[246,944,945],{"class":252},"  },\n",[246,947,948],{"class":248,"line":344},[246,949,950],{"class":252},"};\n",[246,952,953],{"class":248,"line":490},[246,954,487],{"emptyLinePlaceholder":486},[246,956,957,960],{"class":248,"line":508},[246,958,959],{"class":256},"return",[246,961,962],{"class":252}," msg;\n",[211,964,965,966,969,970,973],{},"Finally it gets passed to an influx node which saves it to the ",[243,967,968],{},"CRON"," database under the ",[243,971,972],{},"ip"," measurement",[218,975,977],{"id":976},"grafana","Grafana",[211,979,980],{},"Finally - we can log in to grafana and create a new graph. The query I have used is:",[236,982,987],{"className":983,"code":985,"language":986},[984],"language-text","SELECT count(\"reason\") FROM \"ip\" WHERE $timeFilter GROUP BY time($__interval) fill(0)\n","text",[243,988,985],{"__ignoreMap":241},[211,990,991],{},"And - that together with a line graph - with a line width of 3 and staircase turned on - gives me the following graph (this was taken after running for about 24 hours):",[211,993,994],{},[995,996],"img",{"alt":997,"src":998},"Grafana output","\u002Fimages\u002Fposts\u002F2020\u002F01\u002Fcron-ip-output.png",[211,1000,1001],{},"You can see that there is only a few hits on the first day and none on the second.",[211,1003,1004],{},"So - I'm happy with the ipify service (which is free to use and sets no quota or timing limits) - and yet I also get a nice graph which I likely will never look at after the first week :)",[1006,1007,1008],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":241,"searchDepth":325,"depth":325,"links":1010},[1011,1012,1015,1016,1017],{"id":220,"depth":325,"text":221},{"id":350,"depth":325,"text":351,"children":1013},[1014],{"id":401,"depth":332,"text":402},{"id":855,"depth":325,"text":856},{"id":909,"depth":325,"text":910},{"id":976,"depth":325,"text":977},"2020-01-29 11:49 +0100",null,"md","My ISP connection to home does not offer a static IP address. It does maintain a stable IP address - but after a period offline (either a fault or a power failure or similar) then that IP address may change. How to monitor this?",{},"\u002F2020\u002F01\u002F29\u002Fmonitoring-ip-changes",{"title":206,"description":213},{"loc":1023},"2020\u002F01\u002F29\u002Fmonitoring-ip-changes",[234,350,909,855,1028,976,1029,1030],"mosquitto","influx","cron","zOwDyP69FZUb36PONCcqE4xKW8ugrRTzrBzmaz5rSyY",1775293006950]