wifi-observer 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 biswanathamz
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,391 @@
1
+ Metadata-Version: 2.4
2
+ Name: wifi-observer
3
+ Version: 0.1.0
4
+ Summary: Terminal tool that monitors internet reachability and WiFi signal strength once per second, with a live UI, JSON logging, and matplotlib graphs.
5
+ Author: biswanathamz
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/biswanathamz/wifi-observer
8
+ Project-URL: Repository, https://github.com/biswanathamz/wifi-observer
9
+ Project-URL: Issues, https://github.com/biswanathamz/wifi-observer/issues
10
+ Keywords: wifi,network,monitor,ping,latency,signal,terminal,tui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: System :: Networking :: Monitoring
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: graph
28
+ Requires-Dist: matplotlib; extra == "graph"
29
+ Provides-Extra: dev
30
+ Requires-Dist: ruff; extra == "dev"
31
+ Requires-Dist: pytest; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ <div align="center">
35
+
36
+ # 📶 WiFi Observer
37
+
38
+ **A terminal tool that tells you — every second — whether your problem is the _internet_ or your _WiFi_.**
39
+
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
41
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/)
42
+ [![Platform: Linux](https://img.shields.io/badge/platform-Linux-lightgrey.svg)](#requirements)
43
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing)
44
+ [![Made with: Python & Bash](https://img.shields.io/badge/made%20with-Python%20%26%20Bash-1f425f.svg)](#how-it-works)
45
+
46
+ </div>
47
+
48
+ WiFi Observer continuously monitors two **independent** things — internet
49
+ reachability (via ping) and WiFi signal strength (in dBm) — so you can tell *why*
50
+ your connection is bad, not just *that* it is. It runs entirely in your terminal,
51
+ keeps a live history in memory, logs every sample to JSON, and draws a graph of
52
+ the session. Built with the Python standard library + Bash; the only optional
53
+ dependency is matplotlib (for graphs), installed automatically on first run.
54
+
55
+ ---
56
+
57
+ ## 📑 Table of contents
58
+
59
+ - [Why](#why)
60
+ - [Demo](#demo)
61
+ - [Features](#features)
62
+ - [Installation](#installation)
63
+ - [Usage](#usage)
64
+ - [Configuration](#configuration)
65
+ - [How it works](#how-it-works)
66
+ - [Output & file formats](#output--file-formats)
67
+ - [Reading the numbers](#reading-the-numbers)
68
+ - [Project structure](#project-structure)
69
+ - [Requirements](#requirements)
70
+ - [Roadmap](#roadmap)
71
+ - [Contributing](#contributing)
72
+ - [License](#license)
73
+
74
+ ---
75
+
76
+ ## Why
77
+
78
+ A plain ping test lumps two different failures together. WiFi Observer separates
79
+ them:
80
+
81
+ | WiFi signal | Internet | Likely culprit |
82
+ |-------------|----------|----------------|
83
+ | Strong & stable | Down / lossy | **ISP / router** — not your WiFi |
84
+ | Weak / unstable | Down / lossy | **Your WiFi** — distance, walls, interference |
85
+ | Strong & stable | Fine | All good ✅ |
86
+
87
+ So instead of "the internet is slow," you get an answer you can act on.
88
+
89
+ ---
90
+
91
+ ## Demo
92
+
93
+ ```
94
+ ┌─ WiFi Observer ───────────────────────────────────────────┐
95
+ SSID: HomeNet-5G iface: wlan0
96
+ elapsed: 00:04:12 remaining: 00:05:48
97
+ log → logs/2026-06-28/wifi-215205.jsonl
98
+
99
+ INTERNET ● UP target 8.8.8.8
100
+ latency : last 23.4 ms avg 26.1 min 18.0 max 142.0 ms
101
+ quality : packet loss 0.8% (2/250) jitter 5.2 ms
102
+ outages : 2 downtime 00:04 longest 00:03
103
+
104
+ WIFI SIGNAL -47 dBm (Excellent)
105
+ stability : STABLE (±1.8 dBm) min -52 max -44 avg -47.3 dBm
106
+
107
+ latency ▁▂▃█▂▂▃▃▂·▂▃▂▂ (· = a probe with no reply / outage)
108
+ signal ▅▅▆▆▅▅▄▅▅▆▆▅▄▅
109
+ └────────────────────────────────────────────────────────────┘
110
+ [g] save graph [q] quit (Ctrl+C also quits)
111
+ ```
112
+
113
+ The exported graph stacks **internet latency** (outages marked in red) over
114
+ **WiFi signal strength** (with quality reference lines), sharing a time axis.
115
+
116
+ > 💡 Tip: drop a real screenshot/GIF here (e.g. `docs/demo.png`) before publishing.
117
+
118
+ ---
119
+
120
+ ## Features
121
+
122
+ - 📡 **Two metrics at once** — internet reachability *and* WiFi signal, sampled every second.
123
+ - 🖥️ **Live terminal UI** — single self-refreshing screen with two labelled sections and sparklines.
124
+ - ⏱️ **Pick a duration on start** — 1 min / 10 min / 1 hr / until you close it, with a live countdown.
125
+ - ⌨️ **Interactive keys** — `g` saves a graph instantly, `q` quits.
126
+ - 📝 **JSON logging** — every sample appended (JSON-lines), crash-safe.
127
+ - 📊 **Graphs ("maps")** — matplotlib charts generated from inside the app and on exit.
128
+ - 🗂️ **Date-organised output** — logs, summary, and graph grouped under `logs/YYYY-MM-DD/`.
129
+ - 🔧 **Near-zero setup** — monitor is stdlib-only; matplotlib is auto-installed into a local `.venv`.
130
+ - 🛟 **Degrades gracefully** — no colours? no matplotlib? not a TTY? It still runs and tells you.
131
+
132
+ ---
133
+
134
+ ## Installation
135
+
136
+ ```bash
137
+ # 1. Clone
138
+ git clone https://github.com/biswanathamz/wifi-observer.git
139
+ cd wifi-observer
140
+
141
+ # 2. Run — that's it.
142
+ ./run.sh
143
+ ```
144
+
145
+ There is **no build step**. The monitor uses only the Python 3 standard library
146
+ and the system `ping`. The first run bootstraps a local `.venv` and installs
147
+ `matplotlib` so graphs work (skip with `./run.sh --no-graph-setup`).
148
+
149
+ > Prefer to manage deps yourself? `pip install matplotlib` and run
150
+ > `python3 wifi_observer.py` directly.
151
+
152
+ ### Install as a command (optional)
153
+
154
+ Install it as a proper CLI so `wifi-observer` is on your `PATH`:
155
+
156
+ ```bash
157
+ pip install . # core only (stdlib monitor)
158
+ pip install '.[graph]' # + matplotlib, for the `g` graph feature
159
+ wifi-observer --help
160
+ ```
161
+
162
+ Without the `graph` extra the monitor still runs fully; graphs are simply
163
+ skipped until matplotlib is present.
164
+
165
+ ---
166
+
167
+ ## Usage
168
+
169
+ ```bash
170
+ ./run.sh # ping 8.8.8.8 every 1s
171
+ ./run.sh -H 1.1.1.1 -i 2 # ping Cloudflare, every 2 seconds
172
+ ./run.sh -H 192.168.1.1 # ping your router (tests the LAN link only)
173
+ ./run.sh --no-color # plain text
174
+ ./run.sh --help # full option list
175
+ ```
176
+
177
+ **On start**, choose how long to run:
178
+
179
+ ```
180
+ WiFi Observer — how long should it run?
181
+ [1] 1 minute
182
+ [2] 10 minutes
183
+ [3] 1 hour
184
+ [4] Until I close it
185
+ Select 1-4 (default 4):
186
+ ```
187
+
188
+ **While running**, the UI is interactive:
189
+
190
+ | Key | Action |
191
+ |-----|--------|
192
+ | `g` | Generate & save a graph (PNG) right now |
193
+ | `q` | Quit and print the session summary |
194
+ | `Ctrl+C` | Also quits cleanly |
195
+
196
+ A graph is **also saved automatically on exit**, and everything lands under
197
+ `logs/<today>/`.
198
+
199
+ ---
200
+
201
+ ## Configuration
202
+
203
+ | Flag | Default | Description |
204
+ |------|---------|-------------|
205
+ | `-H, --host` | `8.8.8.8` | Host/IP to ping (an IP avoids DNS; a hostname is resolved by `ping`). |
206
+ | `-i, --interval` | `1.0` | Seconds between samples. |
207
+ | `-t, --timeout` | `1.0` | Per-ping timeout (s) → a slow/no reply counts as DOWN. |
208
+ | `-n, --history` | `3600` | Samples kept in memory for the live UI / sparklines. |
209
+ | `--log PATH` | auto | Custom JSON-lines log path (default `logs/<date>/wifi-<time>.jsonl`). |
210
+ | `--no-log` | off | Disable JSON logging. |
211
+ | `--no-color` | off | Plain text, no ANSI colours. |
212
+ | `--no-graph-setup` | off | *(run.sh)* Skip the matplotlib `.venv` bootstrap. |
213
+
214
+ ---
215
+
216
+ ## How it works
217
+
218
+ ### Architecture
219
+
220
+ | File | Language | Role |
221
+ |------|----------|------|
222
+ | [run.sh](run.sh) | Bash | Checks deps, bootstraps matplotlib into `.venv`, launches the app. |
223
+ | [wifi_observer.py](wifi_observer.py) | Python | Monitor loop, measurements, statistics, interactive UI, JSON logging. |
224
+ | [wifi_observer_plot.py](wifi_observer_plot.py) | Python | Builds the matplotlib figure; used by the app and standalone. |
225
+
226
+ ### The per-second cycle
227
+
228
+ 1. **Ping** the host once → reachable? + latency.
229
+ 2. **Read WiFi signal** from `/proc/net/wireless` → dBm + link quality.
230
+ 3. Build a sample record and append it to the **in-memory history** and the **JSON log**.
231
+ 4. Update **running statistics** incrementally (counts, sums, sums-of-squares).
232
+ 5. **Redraw** the UI.
233
+ 6. Wait out the interval while **polling the keyboard** for `g`/`q`.
234
+
235
+ ### How the ping works
236
+
237
+ No hand-rolled ICMP — it runs the system `ping`:
238
+
239
+ ```
240
+ ping -c 1 -W <timeout> <host>
241
+ ```
242
+
243
+ `-c 1` sends one echo request; `-W` is the reply timeout. UP/DOWN is decided from
244
+ ping's **exit code** (0 = reply); latency is parsed from the output (`time=23.4 ms`)
245
+ with a regex. Because the OS `ping` already holds the ICMP privilege, **no `sudo`
246
+ is needed**. The call is wrapped so a hang or failure can never crash the loop — it
247
+ just records a DOWN sample.
248
+
249
+ ### How the WiFi signal is read
250
+
251
+ The kernel exposes live wireless stats as text in `/proc/net/wireless`. The program
252
+ parses the active interface's row for **link quality** (≈ out of 70 → a 0–100 %
253
+ figure) and **signal level in dBm**. No external command, no privileges.
254
+
255
+ ### Statistics
256
+
257
+ Aggregates are maintained incrementally so each tick is O(1) no matter how long
258
+ you run: uptime %, packet loss %, latency avg/min/max, **jitter** (latency
259
+ std-dev), outage count/total/longest, and signal avg/min/max with **std-dev**
260
+ (which drives the stability verdict).
261
+
262
+ History is a bounded `deque` (default last **3600** samples ≈ 1 h), so memory stays
263
+ capped — while the **JSON log keeps the full session** on disk, and graphs are
264
+ built from that log.
265
+
266
+ ---
267
+
268
+ ## Output & file formats
269
+
270
+ Each run writes into a **date folder**, all artifacts sharing one basename:
271
+
272
+ ```
273
+ logs/
274
+ └── 2026-06-28/
275
+ ├── wifi-215205.jsonl # one JSON object per sample
276
+ ├── wifi-215205.summary.json # session aggregates
277
+ └── wifi-215205.png # the graph ("map")
278
+ ```
279
+
280
+ **Per-sample log line** (`.jsonl`):
281
+
282
+ ```json
283
+ {"ts": 1782662252.80, "time": "2026-06-28T21:27:32.802", "internet_up": true,
284
+ "latency_ms": 47.8, "signal_dbm": -63.0, "signal_quality": 67.1, "ssid": "HomeNet-5G"}
285
+ ```
286
+
287
+ **Summary** (`.summary.json`) — start/end/duration, config, plus `internet` and
288
+ `wifi_signal` blocks (uptime %, packet loss, latency stats, outages, signal
289
+ avg/min/max/stddev/stability).
290
+
291
+ Re-plot any old log standalone:
292
+
293
+ ```bash
294
+ python3 wifi_observer_plot.py # newest log → <log>.png
295
+ python3 wifi_observer_plot.py logs/2026-06-28/wifi-215205.jsonl -o report.png --show
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Reading the numbers
301
+
302
+ **WiFi signal (dBm)** — higher (closer to 0) is better:
303
+
304
+ | dBm | Label | Meaning |
305
+ |-----|-------|---------|
306
+ | ≥ -50 | Excellent | Right next to the router |
307
+ | -50 to -60 | Good | Solid, reliable |
308
+ | -60 to -67 | Fair | Usable; HD video fine |
309
+ | -67 to -75 | Weak | Drops/slowdowns likely |
310
+ | < -75 | Very weak | Often unusable |
311
+
312
+ **Stability** (from signal std-dev): `STABLE` < 3 dBm, `FLUCTUATING` < 6 dBm,
313
+ `UNSTABLE` ≥ 6 dBm. **Latency / jitter / packet loss** — lower is better; high
314
+ jitter hurts calls/gaming even when average latency looks fine.
315
+
316
+ ---
317
+
318
+ ## Project structure
319
+
320
+ ```
321
+ wifi-observer/
322
+ ├── doc/
323
+ │ └── SPEC.md # full specification
324
+ ├── run.sh # Bash launcher (+ matplotlib venv bootstrap)
325
+ ├── wifi_observer.py # Python monitor, UI, logging
326
+ ├── wifi_observer_plot.py # matplotlib figure builder (UI + standalone)
327
+ ├── LICENSE # MIT
328
+ ├── README.md
329
+ ├── .venv/ # auto-created on first run [git-ignored]
330
+ └── logs/ # date folders of output [git-ignored]
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Requirements
336
+
337
+ - **Linux** with `python3` (3.8+) and `ping` on `PATH`. The monitor itself is
338
+ **standard-library only**.
339
+ - **WiFi signal** needs `/proc/net/wireless` (standard on Linux WiFi). SSID/
340
+ interface detection uses `iwgetid`/`nmcli` when present; otherwise it shows `?`.
341
+ - **matplotlib** — only for graphs; `run.sh` installs it into a local `.venv` on
342
+ first run (needs `python3-venv` + network once). Skip with `--no-graph-setup`.
343
+
344
+ > **Platform note:** Linux-focused. The `ping` flags and signal reading target
345
+ > Linux; macOS/Windows support would need contributions (see the roadmap).
346
+
347
+ ---
348
+
349
+ ## Roadmap
350
+
351
+ Ideas and good first contributions:
352
+
353
+ - [ ] macOS / Windows support (`ping` flags + signal reading)
354
+ - [ ] Optional gateway probe to pinpoint LAN-vs-WAN faults
355
+ - [ ] DNS-resolution check (distinguish "internet down" from "DNS down")
356
+ - [ ] Desktop notification on outage start / recovery
357
+ - [ ] CSV export and a `--summary-only` headless mode
358
+ - [ ] Configurable thresholds (signal labels, stability bands)
359
+
360
+ Found a bug or want a feature? Please [open an issue](../../issues).
361
+
362
+ ---
363
+
364
+ ## Contributing
365
+
366
+ Contributions are welcome! 🎉
367
+
368
+ 1. **Fork** the repo and create a branch: `git checkout -b feature/my-change`.
369
+ 2. **Make your change.** Keep the monitor **standard-library only** (matplotlib is
370
+ fine to import *lazily* inside `wifi_observer_plot.py`). Match the existing style.
371
+ 3. **Test it** — run `./run.sh` and verify the live UI, logging, and a generated
372
+ graph. For unreachable-host behaviour, try `./run.sh -H 192.0.2.1` (TEST-NET).
373
+ 4. **Commit** with a clear message and **open a Pull Request** describing what and
374
+ why.
375
+
376
+ Please keep changes focused, document new flags in the README, and be kind in
377
+ code review. By contributing, you agree your work is licensed under the project's
378
+ MIT license.
379
+
380
+ ---
381
+
382
+ ## License
383
+
384
+ Released under the [MIT License](LICENSE) — free to use, modify, and distribute
385
+ with attribution. © 2026 biswanathamz.
386
+
387
+ <div align="center">
388
+
389
+ ⭐ If this saved you a frustrating "is it me or the internet?" moment, consider starring the repo.
390
+
391
+ </div>