From d6a4b4c27eccbbff8b009aa4727138ba9715e3a1 Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 21 Aug 2026 12:38:20 +0200 Subject: [PATCH] Initial release v0.2.0 --- .gitignore | 6 ++ CHANGELOG.md | 26 +++++++ InternetMonitor.ax | 175 +++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 21 ++++++ README.md | 87 ++++++++++++++++++++++ 5 files changed, 315 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 InternetMonitor.ax create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1033811 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +Thumbs.db +.vscode/ +.idea/ +*.tmp +*.swp diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a3dcf01 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +# Changelog + +## [0.2.0] +### Added +- `MY IP` intro +- Animated HSV rainbow effect +- Two complete IP scroll passes +- Configurable scroll speed +- Configurable retry interval +- Configurable offline failure threshold +- Secondary public IP service as fallback +- Hardened ONLINE/OFFLINE detection + +### Changed +- A single failed request no longer causes OFFLINE +- Failed checks use a shorter retry interval +- Successful checks immediately reset the failure counter +- Public IP uses an animated per-character rainbow effect + +## [0.1.0] +### Added +- Initial Internet connectivity monitoring +- Public IPv4 detection +- ONLINE/OFFLINE display +- Configurable refresh interval +- Configurable display colors diff --git a/InternetMonitor.ax b/InternetMonitor.ax new file mode 100644 index 0000000..8cadb1a --- /dev/null +++ b/InternetMonitor.ax @@ -0,0 +1,175 @@ +# @name InternetMonitor +# @desc Public IPv4 and Internet status monitor +# @author Spectral +# @version 0.2.0 +# @config refresh number "Prüfintervall" default=60 min=30 max=1800 unit=s +# @config retry_seconds number "Fehler-Retry" default=15 min=5 max=60 unit=s +# @config fail_threshold number "Offline nach Fehlern" default=3 min=1 max=10 +# @config intro_seconds number "Intro-Dauer" default=3 min=1 max=10 unit=s +# @config view_seconds number "Ansichtsdauer" default=3 min=1 max=10 unit=s +# @config scroll_speed number "IP Scroll-Speed" default=100 min=25 max=200 unit=% +# @config online_color color "Online" default=#00FF00 +# @config offline_color color "Offline" default=#FF0000 +# @config info_color color "Information" default=#FFFFFF + +class InternetMonitor + var url1, url2 + var refreshms, retryms, threshold + var introms, viewms + var speed, onlinec, offlinec, infoc + var state, flight, attempt, fails + var nextcheck + var ip, ipw + var xintro, xon, xoff, xcheck + var phase, phaseat + var scrollat, scrollms + + def init() + self.url1="https://api.ipify.org" + self.url2="https://checkip.global.api.aws/" + self.refreshms=store.get("refresh")*1000 + self.retryms=store.get("retry_seconds")*1000 + self.threshold=store.get("fail_threshold") + self.introms=store.get("intro_seconds")*1000 + self.viewms=store.get("view_seconds")*1000 + self.speed=store.get("scroll_speed") + self.onlinec=store.get("online_color") + self.offlinec=store.get("offline_color") + self.infoc=store.get("info_color") + self.state=0 + self.flight=false + self.attempt=0 + self.fails=0 + self.nextcheck=0 + self.ip=store.get("ip") + if self.ip==nil + self.ip="---.---.---.---" + end + self.ipw=text_width(self.ip) + self.xintro=(width()-text_ink_width("MY IP"))/2 + self.xon=7+(width()-7-text_ink_width("ONLINE"))/2 + self.xoff=7+(width()-7-text_ink_width("OFFLINE"))/2 + self.xcheck=(width()-text_ink_width("CHECK"))/2 + self.phase=0 + self.phaseat=0 + self.scrollat=0 + self.scrollms=0 + end + + def on_body(b,st) + var m=nil + if b!=nil && st>=200 && st<300 + m=re.search("([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)",b) + end + if m==nil + if self.attempt==0 + self.attempt=1 + http.get(self.url2,/ b,s -> self.on_body(b,s)) + return + end + self.flight=false + self.fails+=1 + self.nextcheck=now_ms()+self.retryms + if self.fails>=self.threshold + self.state=-1 + store.set("online",false) + end + return + end + self.flight=false + self.fails=0 + self.state=1 + self.ip=m[1] + self.ipw=text_width(self.ip) + self.nextcheck=now_ms()+self.refreshms + store.set("ip",self.ip) + store.set("online",true) + end + + def loop() + var n=now_ms() + if !self.flight && n>=self.nextcheck + self.flight=true + self.attempt=0 + self.nextcheck=n+self.refreshms + http.get(self.url1,/ b,s -> self.on_body(b,s)) + end + if self.phase==1 + if n-self.phaseat>=self.introms + self.phase=2 + self.phaseat=n + end + elif self.phase==2 + if n-self.phaseat>=self.viewms + if self.state==1 + self.phase=3 + self.scrollat=n + var pxps=21*self.speed/100 + if pxps<1 + pxps=1 + end + var run=width()+self.ipw + self.scrollms=int(run*1000/pxps)*2 + else + self.phase=0 + rotation.resume() + rotation.next() + end + end + elif self.phase==3 + if n-self.scrollat>=self.scrollms + self.phase=0 + rotation.resume() + rotation.next() + end + end + end + + def on_show() + self.phase=1 + self.phaseat=now_ms() + rotation.pause() + end + + def draw() + clear() + if self.phase==1 + text(self.xintro,6,"MY IP",self.infoc) + return + end + if self.state==0 + text(self.xcheck,6,"CHECK",self.infoc) + return + end + if self.state<0 + line(1,2,5,6,self.offlinec) + line(1,6,5,2,self.offlinec) + text(self.xoff,6,"OFFLINE",self.offlinec) + return + end + if self.phase==2 + line(1,4,2,5,self.onlinec) + line(2,5,5,2,self.onlinec) + text(self.xon,6,"ONLINE",self.onlinec) + return + end + if self.phase==3 + var n=now_ms() + var pxps=21*self.speed/100 + if pxps<1 + pxps=1 + end + var run=width()+self.ipw + var runms=int(run*1000/pxps) + var elapsed=(n-self.scrollat)%runms + var px=int(elapsed*pxps/1000) + var x=width()-px + var ph=int(elapsed/20)%360 + for i : 0 .. size(self.ip)-1 + x+=text(x,6,self.ip[i],hsv((ph+i*30)%360,100,100)) + end + end + end +end + +return InternetMonitor() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..01ce5e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Spectral + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b86f760 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# AWTRIX NG Internet Monitor + +Native AWTRIX NG script that monitors Internet connectivity and displays the current public IPv4 address directly on the device. + +No Home Assistant, Node-RED, MQTT, ioBroker or API key is required. + +## Features +- Public IPv4 display +- ONLINE / OFFLINE status +- Animated HSV rainbow effect for the public IP +- Two complete IP scroll passes +- Primary IP service plus AWS fallback +- Hardened offline detection +- Faster retry interval after failed checks +- Last known public IPv4 stored locally +- Configurable timings, scroll speed and colors +- No API key required + +## Display sequence + +Online: + +```text +MY IP + ↓ +✓ ONLINE + ↓ +Public IPv4 with animated rainbow effect +(two complete scroll passes) +``` + +Offline: + +```text +MY IP + ↓ +✕ OFFLINE +``` + +## Reliability +A single failed HTTP request does not immediately mean the Internet connection is offline. Each failed check cycle tries both services. By default, three consecutive failed cycles are required before `OFFLINE` is shown. After a failed cycle, the script retries after 15 seconds. Any successful check immediately resets the failure counter. + +## Configuration + +| Setting | Default | Description | +|---|---:|---| +| `refresh` | 60 s | Normal check interval | +| `retry_seconds` | 15 s | Retry interval after a failed cycle | +| `fail_threshold` | 3 | Failed cycles required before OFFLINE | +| `intro_seconds` | 3 s | Duration of `MY IP` | +| `view_seconds` | 3 s | Duration of ONLINE/OFFLINE | +| `scroll_speed` | 100 % | Public IP scroll speed | +| `online_color` | `#00FF00` | ONLINE color | +| `offline_color` | `#FF0000` | OFFLINE color | +| `info_color` | `#FFFFFF` | Neutral information color | + +## Public IP services +1. `https://api.ipify.org` +2. `https://checkip.global.api.aws/` as fallback + +## Privacy +The external IP-check services necessarily receive the public source IP address of the request. The script does not intentionally transmit additional personal data. + +## Requirements +- AWTRIX NG +- Internet access from the device +- IPv4 connectivity + +## IPv6 +Version 0.2.0 supports public IPv4 only. + +## Installation +1. Open the AWTRIX NG web interface. +2. Go to **Scripts**. +3. Create a script named `InternetMonitor`. +4. Paste `InternetMonitor.ax`. +5. Save it. +6. Configure it under **Apps → InternetMonitor → Settings** if required. + +## Version +**0.2.0** + +## Author +Spectral + +## License +MIT