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.
- wifi_observer-0.1.0/LICENSE +21 -0
- wifi_observer-0.1.0/PKG-INFO +391 -0
- wifi_observer-0.1.0/README.md +358 -0
- wifi_observer-0.1.0/pyproject.toml +49 -0
- wifi_observer-0.1.0/setup.cfg +4 -0
- wifi_observer-0.1.0/tests/test_basic.py +84 -0
- wifi_observer-0.1.0/tests/test_core.py +269 -0
- wifi_observer-0.1.0/tests/test_ping_regex.py +26 -0
- wifi_observer-0.1.0/tests/test_statistics.py +48 -0
- wifi_observer-0.1.0/tests/test_wireless.py +25 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/PKG-INFO +391 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/SOURCES.txt +16 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/dependency_links.txt +1 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/entry_points.txt +2 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/requires.txt +7 -0
- wifi_observer-0.1.0/wifi_observer.egg-info/top_level.txt +2 -0
- wifi_observer-0.1.0/wifi_observer.py +652 -0
- wifi_observer-0.1.0/wifi_observer_plot.py +138 -0
|
@@ -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)
|
|
41
|
+
[](https://www.python.org/)
|
|
42
|
+
[](#requirements)
|
|
43
|
+
[](#contributing)
|
|
44
|
+
[](#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>
|