pingmonitor 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pingmonitor-1.0.0/.gitignore +18 -0
- pingmonitor-1.0.0/LICENSE +21 -0
- pingmonitor-1.0.0/PKG-INFO +224 -0
- pingmonitor-1.0.0/README.md +201 -0
- pingmonitor-1.0.0/docs/advisor.svg +244 -0
- pingmonitor-1.0.0/docs/screenshot.svg +246 -0
- pingmonitor-1.0.0/docs/traceroute.svg +253 -0
- pingmonitor-1.0.0/packaging/pingmon.rb +34 -0
- pingmonitor-1.0.0/packaging/pingmon.spec +43 -0
- pingmonitor-1.0.0/pingmon/__init__.py +3 -0
- pingmonitor-1.0.0/pingmon/__main__.py +4 -0
- pingmonitor-1.0.0/pingmon/app.py +948 -0
- pingmonitor-1.0.0/pingmon/app.tcss +183 -0
- pingmonitor-1.0.0/pingmon/config.py +163 -0
- pingmonitor-1.0.0/pingmon/netutil.py +139 -0
- pingmonitor-1.0.0/pingmon/pinger.py +44 -0
- pingmonitor-1.0.0/pingmon/render.py +129 -0
- pingmonitor-1.0.0/pingmon/scoring.py +70 -0
- pingmonitor-1.0.0/pingmon/stats.py +150 -0
- pingmonitor-1.0.0/pyproject.toml +39 -0
- pingmonitor-1.0.0/research/feature-ideas-2026-06-29.md +118 -0
- pingmonitor-1.0.0/research/host-selection-2026-06-29.md +88 -0
- pingmonitor-1.0.0/research/packaging-native-install-2026-06-29.md +76 -0
- pingmonitor-1.0.0/run.sh +12 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# pingmon runtime config (machine-specific; lives in ~/.config/pingmon)
|
|
12
|
+
/config.toml
|
|
13
|
+
|
|
14
|
+
# OS / editor
|
|
15
|
+
.DS_Store
|
|
16
|
+
*.swp
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pingmon contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pingmonitor
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: TUI monitor of latency and availability to servers by country
|
|
5
|
+
Project-URL: Homepage, https://github.com/kottot13/pingmon
|
|
6
|
+
Project-URL: Repository, https://github.com/kottot13/pingmon
|
|
7
|
+
Author: pingmon contributors
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: geoip,latency,monitoring,network,ping,textual,tui
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Classifier: Topic :: System :: Networking :: Monitoring
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: textual>=0.80
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# pingmon
|
|
25
|
+
|
|
26
|
+
A full-featured terminal UI for monitoring **latency and availability** to
|
|
27
|
+
servers in different countries. Built with [Textual](https://textual.textualize.io/).
|
|
28
|
+
|
|
29
|
+
It continuously TCP-pings a set of targets (one or more reachable hosts per
|
|
30
|
+
country), and shows a live, colour-coded dashboard with per-target latency,
|
|
31
|
+
average, jitter, packet loss, a status indicator, and an animated trend graph.
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Live dashboard** — sortable table of targets with status dot, latency, average, loss and an inline coloured sparkline trend.
|
|
38
|
+
- **Detail panel** — for the selected target: live latency graph, quality gauge, min / max / avg / jitter / loss, **MOS** call-quality score, resolved IP, **GeoIP city / region and the hosting network (ASN + ISP)**, sample count.
|
|
39
|
+
- **★ Region Advisor** *(unique)* — ranks regions by a composite 0–100 score under a chosen **use-case profile** (VoIP / Gaming / Web / Bulk) and highlights the best region to pick right now. Press `g`. See [below](#region-advisor).
|
|
40
|
+
- **MOS / R-factor** — turns latency + jitter + loss into the single VoIP call-quality number (ITU-T E-model), the same metric paid monitoring suites charge for.
|
|
41
|
+
- **Threshold alerts** — fire when a target stays slow or lossy for N samples: terminal bell, an in-app toast, an OS desktop notification, and a blinking row marker. Auto-clears on recovery.
|
|
42
|
+
- **Traceroute drill-down** — press `Enter` (or click) on a row for an mtr-style hop-by-hop path with per-hop loss and per-probe timings, plus **GeoIP per hop** (flag, city and ASN) so you can see which countries and networks the traffic crosses.
|
|
43
|
+
- **Per-country set, ready to go** — the Netherlands, Germany, United Kingdom, France, Cyprus, Italy, Spain, Greece, Sweden, Ireland and the United States are pre-configured with reachable hosts.
|
|
44
|
+
- **Editable targets** — add (`a`), edit (`E`) and delete (`d`) targets right inside the TUI, or edit the TOML config by hand; reset stats with `r`. In-app changes are saved to the config immediately.
|
|
45
|
+
- **GeoIP auto-detect & enrichment** — every target is looked up via ip-api.com: the detail panel shows its **city, region and hosting network (ASN + ISP)**, and when you add a target with country/flag left blank they are filled in automatically.
|
|
46
|
+
- **Show filter** — flip the table between **all**, **mine only** (targets you added) and **others only** (the built-in set) with `f`.
|
|
47
|
+
- **No root required** — uses TCP connect timing (port 443/80), so it works without raw-socket / ICMP privileges and measures real service latency.
|
|
48
|
+
- **Modern terminal UX** — truecolor, mouse support, zebra-striped table, a live "heartbeat" spinner, keyboard and mouse navigation, sort modes and modal dialogs.
|
|
49
|
+
|
|
50
|
+
| Region Advisor | Traceroute drill-down |
|
|
51
|
+
| -------------- | --------------------- |
|
|
52
|
+
|  |  |
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.11+ (not needed for the standalone binary below)
|
|
57
|
+
- `textual >= 0.80` (installed automatically)
|
|
58
|
+
|
|
59
|
+
## Install as a system command
|
|
60
|
+
|
|
61
|
+
Use it like `htop` — install once, run `pingmon` from anywhere.
|
|
62
|
+
|
|
63
|
+
### pipx / uv (recommended, Linux + macOS)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pipx install pingmon # from PyPI once published
|
|
67
|
+
pipx install . # or from a checkout of this repo
|
|
68
|
+
pipx install git+https://github.com/kottot13/pingmon
|
|
69
|
+
# uv works the same: uv tool install pingmon / uvx pingmon
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`pipx` keeps pingmon in its own isolated environment and puts the `pingmon`
|
|
73
|
+
command on your `PATH`.
|
|
74
|
+
|
|
75
|
+
### Homebrew (macOS / Linuxbrew)
|
|
76
|
+
|
|
77
|
+
A formula skeleton lives in [`packaging/pingmon.rb`](packaging/pingmon.rb).
|
|
78
|
+
Publish to PyPI, fill in the sdist URL + `brew update-python-resources`, then:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
brew install kottot13/tap/pingmon
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Standalone binary (no Python on the target)
|
|
85
|
+
|
|
86
|
+
The most htop-like option — a single executable built with PyInstaller
|
|
87
|
+
([`packaging/pingmon.spec`](packaging/pingmon.spec)):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install pyinstaller
|
|
91
|
+
pyinstaller packaging/pingmon.spec
|
|
92
|
+
sudo cp dist/pingmon /usr/local/bin/ # macOS
|
|
93
|
+
cp dist/pingmon ~/.local/bin/ # Linux
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Build once per OS/architecture (PyInstaller does not cross-compile).
|
|
97
|
+
|
|
98
|
+
### From source (development)
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
./run.sh # makes a local venv, installs Textual, launches
|
|
102
|
+
# or
|
|
103
|
+
python3 -m venv .venv && .venv/bin/pip install -e . && .venv/bin/pingmon
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Config lives at `~/.config/pingmon/config.toml` by default (or a local
|
|
107
|
+
`./config.toml` if present, or `$PINGMON_CONFIG`). Run `pingmon --help` for the
|
|
108
|
+
CLI flags (`-V/--version`, `-c/--config PATH`).
|
|
109
|
+
|
|
110
|
+
## Keys
|
|
111
|
+
|
|
112
|
+
| Key | Action |
|
|
113
|
+
| -------------- | ------------------------------- |
|
|
114
|
+
| `↑ / ↓`, `j/k` | Move selection |
|
|
115
|
+
| `Enter` | Traceroute drill-down for the selected target |
|
|
116
|
+
| `g` | Open the Region Advisor (`[` `]` / `p` switch profile inside) |
|
|
117
|
+
| `p` | Cycle the Advisor profile (VoIP / Gaming / Web / Bulk) |
|
|
118
|
+
| `f` | Cycle the show filter (all / mine only / others only) |
|
|
119
|
+
| `space` | Pause / resume probing |
|
|
120
|
+
| `m` / `M` | Sort by latency (ms); press again to flip fastest ⇄ slowest first |
|
|
121
|
+
| `s` | Cycle sort (country / latency / loss / jitter) |
|
|
122
|
+
| `a` | Add a target |
|
|
123
|
+
| `E` | Edit the selected target (form pre-filled) |
|
|
124
|
+
| `A` | Toggle the alert system on / off |
|
|
125
|
+
| `d` | Delete the selected target |
|
|
126
|
+
| `r` | Reset all statistics |
|
|
127
|
+
| `e` | Show the config file path |
|
|
128
|
+
| `q` | Quit |
|
|
129
|
+
|
|
130
|
+
## Region Advisor
|
|
131
|
+
|
|
132
|
+
The Advisor (`g`) answers the question the tool was born from — *which region
|
|
133
|
+
should I actually pick right now?* It computes a **0–100 score** per region from
|
|
134
|
+
latency, jitter and loss, under a selectable **profile**:
|
|
135
|
+
|
|
136
|
+
| Profile | What it optimises for |
|
|
137
|
+
| ------- | --------------------- |
|
|
138
|
+
| **VoIP / Video call** | jitter & loss dominate; driven by the MOS / E-model score |
|
|
139
|
+
| **Gaming** | raw latency + jitter, loss punished hard |
|
|
140
|
+
| **Web / API** | latency-led, mild loss penalty |
|
|
141
|
+
| **Bulk / Backup** | loss-led, tolerant of high latency |
|
|
142
|
+
|
|
143
|
+
Regions are ranked best-first with the #1 pick highlighted, each with a score
|
|
144
|
+
bar, a letter grade (A–F) and a one-line reason. Switch profile with `[` / `]`,
|
|
145
|
+
`p` or `Tab`; close with `Esc`.
|
|
146
|
+
|
|
147
|
+
## Alerts
|
|
148
|
+
|
|
149
|
+
A target enters **alert** state when, for `alert_window` consecutive samples, it
|
|
150
|
+
is unreachable, slower than `alert_latency` ms, or its recent loss exceeds
|
|
151
|
+
`alert_loss` %. On entry pingmon rings the terminal bell, shows a toast, raises
|
|
152
|
+
an OS desktop notification (macOS `osascript` / Linux `notify-send`, toggle with
|
|
153
|
+
`desktop_notify`) and blinks the row's status marker; it auto-clears with a
|
|
154
|
+
"Recovered" toast. Press `A` to switch the whole alert system off or on at any
|
|
155
|
+
time (the banner shows `⚲ alerts off` while disabled); tune or permanently
|
|
156
|
+
disable the triggers in `config.toml`.
|
|
157
|
+
|
|
158
|
+
## Status colours
|
|
159
|
+
|
|
160
|
+
| Status | Meaning (last sample) |
|
|
161
|
+
| ----------- | ----------------------------- |
|
|
162
|
+
| `EXCELLENT` | < 40 ms |
|
|
163
|
+
| `GOOD` | < 90 ms |
|
|
164
|
+
| `FAIR` | < 180 ms |
|
|
165
|
+
| `POOR` | < 350 ms / ≥ 350 ms |
|
|
166
|
+
| `UNSTABLE` | loss ≥ 20% or a recent drop |
|
|
167
|
+
| `DOWN` | 3+ consecutive failures |
|
|
168
|
+
| `PENDING` | no samples yet |
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
On first run a `config.toml` is created at `~/.config/pingmon/config.toml` (or a
|
|
173
|
+
local `./config.toml` if one already exists, or wherever `$PINGMON_CONFIG` /
|
|
174
|
+
`--config` points). It is plain TOML and meant to be hand-edited:
|
|
175
|
+
|
|
176
|
+
```toml
|
|
177
|
+
interval = 2.0 # poll period per target, seconds
|
|
178
|
+
timeout = 2.0 # TCP connect timeout, seconds
|
|
179
|
+
history = 90 # samples kept in memory for the graph
|
|
180
|
+
|
|
181
|
+
alert_latency = 300.0 # alert if latency stays above this (ms); 0 disables
|
|
182
|
+
alert_loss = 20.0 # alert if recent loss exceeds this (%); 0 disables
|
|
183
|
+
alert_window = 3 # consecutive bad samples before an alert fires
|
|
184
|
+
desktop_notify = true # also raise an OS desktop notification on alert
|
|
185
|
+
|
|
186
|
+
[[targets]]
|
|
187
|
+
country = "Netherlands"
|
|
188
|
+
flag = "🇳🇱"
|
|
189
|
+
host = "speedtest.ams1.nl.leaseweb.net"
|
|
190
|
+
port = 80
|
|
191
|
+
source = "builtin" # "builtin" (shipped) or "user" (added in-app) — drives the `f` filter
|
|
192
|
+
|
|
193
|
+
[[targets]]
|
|
194
|
+
country = "United States"
|
|
195
|
+
flag = "🇺🇸"
|
|
196
|
+
host = "speedtest.newark.linode.com"
|
|
197
|
+
port = 443
|
|
198
|
+
source = "builtin"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Add as many `[[targets]]` blocks as you like; any host or IP works, and the
|
|
202
|
+
port is per-target. `source` is optional — if omitted it is inferred (hosts in
|
|
203
|
+
the built-in set are `builtin`, everything else `user`).
|
|
204
|
+
|
|
205
|
+
## How latency is measured
|
|
206
|
+
|
|
207
|
+
`pingmon` opens a TCP connection to `host:port` and times the round-trip of the
|
|
208
|
+
connection handshake (SYN → SYN/ACK). That is close to true network RTT and,
|
|
209
|
+
unlike ICMP, needs no elevated privileges and reflects whether the service port
|
|
210
|
+
is actually answering. Failed or timed-out connects count as packet loss.
|
|
211
|
+
|
|
212
|
+
## Project layout
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
pingmon/
|
|
216
|
+
app.py # Textual app: table, detail panel, graphs, advisor, alerts, actions
|
|
217
|
+
pinger.py # async TCP ping + DNS resolve
|
|
218
|
+
stats.py # rolling per-target stats (latency, jitter, loss, MOS, status)
|
|
219
|
+
scoring.py # Region Advisor: composite score + use-case profiles
|
|
220
|
+
netutil.py # async traceroute + OS desktop notifications
|
|
221
|
+
render.py # colours, status meta, text sparklines
|
|
222
|
+
config.py # TOML load/save + built-in per-country target set
|
|
223
|
+
app.tcss # dark theme / layout
|
|
224
|
+
```
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# pingmon
|
|
2
|
+
|
|
3
|
+
A full-featured terminal UI for monitoring **latency and availability** to
|
|
4
|
+
servers in different countries. Built with [Textual](https://textual.textualize.io/).
|
|
5
|
+
|
|
6
|
+
It continuously TCP-pings a set of targets (one or more reachable hosts per
|
|
7
|
+
country), and shows a live, colour-coded dashboard with per-target latency,
|
|
8
|
+
average, jitter, packet loss, a status indicator, and an animated trend graph.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Live dashboard** — sortable table of targets with status dot, latency, average, loss and an inline coloured sparkline trend.
|
|
15
|
+
- **Detail panel** — for the selected target: live latency graph, quality gauge, min / max / avg / jitter / loss, **MOS** call-quality score, resolved IP, **GeoIP city / region and the hosting network (ASN + ISP)**, sample count.
|
|
16
|
+
- **★ Region Advisor** *(unique)* — ranks regions by a composite 0–100 score under a chosen **use-case profile** (VoIP / Gaming / Web / Bulk) and highlights the best region to pick right now. Press `g`. See [below](#region-advisor).
|
|
17
|
+
- **MOS / R-factor** — turns latency + jitter + loss into the single VoIP call-quality number (ITU-T E-model), the same metric paid monitoring suites charge for.
|
|
18
|
+
- **Threshold alerts** — fire when a target stays slow or lossy for N samples: terminal bell, an in-app toast, an OS desktop notification, and a blinking row marker. Auto-clears on recovery.
|
|
19
|
+
- **Traceroute drill-down** — press `Enter` (or click) on a row for an mtr-style hop-by-hop path with per-hop loss and per-probe timings, plus **GeoIP per hop** (flag, city and ASN) so you can see which countries and networks the traffic crosses.
|
|
20
|
+
- **Per-country set, ready to go** — the Netherlands, Germany, United Kingdom, France, Cyprus, Italy, Spain, Greece, Sweden, Ireland and the United States are pre-configured with reachable hosts.
|
|
21
|
+
- **Editable targets** — add (`a`), edit (`E`) and delete (`d`) targets right inside the TUI, or edit the TOML config by hand; reset stats with `r`. In-app changes are saved to the config immediately.
|
|
22
|
+
- **GeoIP auto-detect & enrichment** — every target is looked up via ip-api.com: the detail panel shows its **city, region and hosting network (ASN + ISP)**, and when you add a target with country/flag left blank they are filled in automatically.
|
|
23
|
+
- **Show filter** — flip the table between **all**, **mine only** (targets you added) and **others only** (the built-in set) with `f`.
|
|
24
|
+
- **No root required** — uses TCP connect timing (port 443/80), so it works without raw-socket / ICMP privileges and measures real service latency.
|
|
25
|
+
- **Modern terminal UX** — truecolor, mouse support, zebra-striped table, a live "heartbeat" spinner, keyboard and mouse navigation, sort modes and modal dialogs.
|
|
26
|
+
|
|
27
|
+
| Region Advisor | Traceroute drill-down |
|
|
28
|
+
| -------------- | --------------------- |
|
|
29
|
+
|  |  |
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Python 3.11+ (not needed for the standalone binary below)
|
|
34
|
+
- `textual >= 0.80` (installed automatically)
|
|
35
|
+
|
|
36
|
+
## Install as a system command
|
|
37
|
+
|
|
38
|
+
Use it like `htop` — install once, run `pingmon` from anywhere.
|
|
39
|
+
|
|
40
|
+
### pipx / uv (recommended, Linux + macOS)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pipx install pingmon # from PyPI once published
|
|
44
|
+
pipx install . # or from a checkout of this repo
|
|
45
|
+
pipx install git+https://github.com/kottot13/pingmon
|
|
46
|
+
# uv works the same: uv tool install pingmon / uvx pingmon
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`pipx` keeps pingmon in its own isolated environment and puts the `pingmon`
|
|
50
|
+
command on your `PATH`.
|
|
51
|
+
|
|
52
|
+
### Homebrew (macOS / Linuxbrew)
|
|
53
|
+
|
|
54
|
+
A formula skeleton lives in [`packaging/pingmon.rb`](packaging/pingmon.rb).
|
|
55
|
+
Publish to PyPI, fill in the sdist URL + `brew update-python-resources`, then:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
brew install kottot13/tap/pingmon
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Standalone binary (no Python on the target)
|
|
62
|
+
|
|
63
|
+
The most htop-like option — a single executable built with PyInstaller
|
|
64
|
+
([`packaging/pingmon.spec`](packaging/pingmon.spec)):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install pyinstaller
|
|
68
|
+
pyinstaller packaging/pingmon.spec
|
|
69
|
+
sudo cp dist/pingmon /usr/local/bin/ # macOS
|
|
70
|
+
cp dist/pingmon ~/.local/bin/ # Linux
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Build once per OS/architecture (PyInstaller does not cross-compile).
|
|
74
|
+
|
|
75
|
+
### From source (development)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
./run.sh # makes a local venv, installs Textual, launches
|
|
79
|
+
# or
|
|
80
|
+
python3 -m venv .venv && .venv/bin/pip install -e . && .venv/bin/pingmon
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Config lives at `~/.config/pingmon/config.toml` by default (or a local
|
|
84
|
+
`./config.toml` if present, or `$PINGMON_CONFIG`). Run `pingmon --help` for the
|
|
85
|
+
CLI flags (`-V/--version`, `-c/--config PATH`).
|
|
86
|
+
|
|
87
|
+
## Keys
|
|
88
|
+
|
|
89
|
+
| Key | Action |
|
|
90
|
+
| -------------- | ------------------------------- |
|
|
91
|
+
| `↑ / ↓`, `j/k` | Move selection |
|
|
92
|
+
| `Enter` | Traceroute drill-down for the selected target |
|
|
93
|
+
| `g` | Open the Region Advisor (`[` `]` / `p` switch profile inside) |
|
|
94
|
+
| `p` | Cycle the Advisor profile (VoIP / Gaming / Web / Bulk) |
|
|
95
|
+
| `f` | Cycle the show filter (all / mine only / others only) |
|
|
96
|
+
| `space` | Pause / resume probing |
|
|
97
|
+
| `m` / `M` | Sort by latency (ms); press again to flip fastest ⇄ slowest first |
|
|
98
|
+
| `s` | Cycle sort (country / latency / loss / jitter) |
|
|
99
|
+
| `a` | Add a target |
|
|
100
|
+
| `E` | Edit the selected target (form pre-filled) |
|
|
101
|
+
| `A` | Toggle the alert system on / off |
|
|
102
|
+
| `d` | Delete the selected target |
|
|
103
|
+
| `r` | Reset all statistics |
|
|
104
|
+
| `e` | Show the config file path |
|
|
105
|
+
| `q` | Quit |
|
|
106
|
+
|
|
107
|
+
## Region Advisor
|
|
108
|
+
|
|
109
|
+
The Advisor (`g`) answers the question the tool was born from — *which region
|
|
110
|
+
should I actually pick right now?* It computes a **0–100 score** per region from
|
|
111
|
+
latency, jitter and loss, under a selectable **profile**:
|
|
112
|
+
|
|
113
|
+
| Profile | What it optimises for |
|
|
114
|
+
| ------- | --------------------- |
|
|
115
|
+
| **VoIP / Video call** | jitter & loss dominate; driven by the MOS / E-model score |
|
|
116
|
+
| **Gaming** | raw latency + jitter, loss punished hard |
|
|
117
|
+
| **Web / API** | latency-led, mild loss penalty |
|
|
118
|
+
| **Bulk / Backup** | loss-led, tolerant of high latency |
|
|
119
|
+
|
|
120
|
+
Regions are ranked best-first with the #1 pick highlighted, each with a score
|
|
121
|
+
bar, a letter grade (A–F) and a one-line reason. Switch profile with `[` / `]`,
|
|
122
|
+
`p` or `Tab`; close with `Esc`.
|
|
123
|
+
|
|
124
|
+
## Alerts
|
|
125
|
+
|
|
126
|
+
A target enters **alert** state when, for `alert_window` consecutive samples, it
|
|
127
|
+
is unreachable, slower than `alert_latency` ms, or its recent loss exceeds
|
|
128
|
+
`alert_loss` %. On entry pingmon rings the terminal bell, shows a toast, raises
|
|
129
|
+
an OS desktop notification (macOS `osascript` / Linux `notify-send`, toggle with
|
|
130
|
+
`desktop_notify`) and blinks the row's status marker; it auto-clears with a
|
|
131
|
+
"Recovered" toast. Press `A` to switch the whole alert system off or on at any
|
|
132
|
+
time (the banner shows `⚲ alerts off` while disabled); tune or permanently
|
|
133
|
+
disable the triggers in `config.toml`.
|
|
134
|
+
|
|
135
|
+
## Status colours
|
|
136
|
+
|
|
137
|
+
| Status | Meaning (last sample) |
|
|
138
|
+
| ----------- | ----------------------------- |
|
|
139
|
+
| `EXCELLENT` | < 40 ms |
|
|
140
|
+
| `GOOD` | < 90 ms |
|
|
141
|
+
| `FAIR` | < 180 ms |
|
|
142
|
+
| `POOR` | < 350 ms / ≥ 350 ms |
|
|
143
|
+
| `UNSTABLE` | loss ≥ 20% or a recent drop |
|
|
144
|
+
| `DOWN` | 3+ consecutive failures |
|
|
145
|
+
| `PENDING` | no samples yet |
|
|
146
|
+
|
|
147
|
+
## Configuration
|
|
148
|
+
|
|
149
|
+
On first run a `config.toml` is created at `~/.config/pingmon/config.toml` (or a
|
|
150
|
+
local `./config.toml` if one already exists, or wherever `$PINGMON_CONFIG` /
|
|
151
|
+
`--config` points). It is plain TOML and meant to be hand-edited:
|
|
152
|
+
|
|
153
|
+
```toml
|
|
154
|
+
interval = 2.0 # poll period per target, seconds
|
|
155
|
+
timeout = 2.0 # TCP connect timeout, seconds
|
|
156
|
+
history = 90 # samples kept in memory for the graph
|
|
157
|
+
|
|
158
|
+
alert_latency = 300.0 # alert if latency stays above this (ms); 0 disables
|
|
159
|
+
alert_loss = 20.0 # alert if recent loss exceeds this (%); 0 disables
|
|
160
|
+
alert_window = 3 # consecutive bad samples before an alert fires
|
|
161
|
+
desktop_notify = true # also raise an OS desktop notification on alert
|
|
162
|
+
|
|
163
|
+
[[targets]]
|
|
164
|
+
country = "Netherlands"
|
|
165
|
+
flag = "🇳🇱"
|
|
166
|
+
host = "speedtest.ams1.nl.leaseweb.net"
|
|
167
|
+
port = 80
|
|
168
|
+
source = "builtin" # "builtin" (shipped) or "user" (added in-app) — drives the `f` filter
|
|
169
|
+
|
|
170
|
+
[[targets]]
|
|
171
|
+
country = "United States"
|
|
172
|
+
flag = "🇺🇸"
|
|
173
|
+
host = "speedtest.newark.linode.com"
|
|
174
|
+
port = 443
|
|
175
|
+
source = "builtin"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Add as many `[[targets]]` blocks as you like; any host or IP works, and the
|
|
179
|
+
port is per-target. `source` is optional — if omitted it is inferred (hosts in
|
|
180
|
+
the built-in set are `builtin`, everything else `user`).
|
|
181
|
+
|
|
182
|
+
## How latency is measured
|
|
183
|
+
|
|
184
|
+
`pingmon` opens a TCP connection to `host:port` and times the round-trip of the
|
|
185
|
+
connection handshake (SYN → SYN/ACK). That is close to true network RTT and,
|
|
186
|
+
unlike ICMP, needs no elevated privileges and reflects whether the service port
|
|
187
|
+
is actually answering. Failed or timed-out connects count as packet loss.
|
|
188
|
+
|
|
189
|
+
## Project layout
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
pingmon/
|
|
193
|
+
app.py # Textual app: table, detail panel, graphs, advisor, alerts, actions
|
|
194
|
+
pinger.py # async TCP ping + DNS resolve
|
|
195
|
+
stats.py # rolling per-target stats (latency, jitter, loss, MOS, status)
|
|
196
|
+
scoring.py # Region Advisor: composite score + use-case profiles
|
|
197
|
+
netutil.py # async traceroute + OS desktop notifications
|
|
198
|
+
render.py # colours, status meta, text sparklines
|
|
199
|
+
config.py # TOML load/save + built-in per-country target set
|
|
200
|
+
app.tcss # dark theme / layout
|
|
201
|
+
```
|