Contribute
Wavelens knows what it knows because people drove around with it and told us what they saw. Every town has gear ours hasn't met. Here are the three ways to add yours, from “two taps” to “write the rule yourself”. Nothing here needs an account; the app has no network access and never sends anything by itself.
Three ways, by effort
- Email an export from the app. Two taps. We do the rest.
- Open a pull request on the public repo with a fixed rule, a new rule, a region pack or a RadioReference-derived band table.
- Write a signature pack of your own, use it on your phone today, and send it in when it's proven.
1. Email an export (the easy way)
In the app: More → Help improve Wavelens where you live. It builds one text file with everything the phone has seen — every emitter with its address, name, maker, category, which rules matched and the signal; the unknowns report; the alert log; your corrections and flags; tail records; a diagnostics summary — and opens your email app with it attached to wavelensapp@gmail.com. Nothing is sent until you press send.
- Positions are off by default and rounded to about 100 m when you turn them on.
- Devices you marked Mine are left out unless you include them.
- The most useful single thing in it is your corrections: every time you tapped Wrong? Correct it and said “ambulance” or “bus”, that's a labelled example we can turn into a rule for everyone.
- Add a line in the note box: what town, what you were driving past, anything the app got wrong.
We use the file to improve the signatures and the region pack for your area, then delete it. It never goes anywhere else.
2. A pull request
The public repo is github.com/pmcbrien/wavelens. The parts that take contributions:
| Folder | What's there | A good PR |
|---|---|---|
packs/ | Region packs, one per country (nz.json), the template and the checker | A new country, or a fix to a band table, with the regulator's allocation table linked in the description |
companion/ | The ESP32 firmware, the Pi bridge, the admin page, the diagrams | A decoder fix, a new board's pin map, a clearer error in the journal |
packs/README.md, companion/README.md | The docs | Anything that confused you and shouldn't have |
Signature rules (the device fingerprints) live in the app and are not in the public repo yet; send those by email or as a pack file (next section) and we fold them in with credit. Run python3 packs/validate.py yourfile.json before opening the PR; the app runs the same checks on import, so a file that passes there imports cleanly.
3. Your own signature pack
A signature pack is a JSON file of rules. Each rule says: when a device matches this (a maker's address prefix, a Bluetooth company ID, an advertised name, a service UUID, the first bytes of a payload), call it that (a category), with this much confidence. The app ships one built-in pack (283 rules as of 6 November) and merges yours on top; a rule in your pack with the same id as a built-in one replaces it. Import under More → Signatures → Import; switch back to the built-in pack on the same screen any time.
{
"version": 1,
"updated": "2026-11-03",
"pack_name": "My town",
"rules": [ ...rules... ]
}
A rule, field by field
| Field | Required | Meaning |
|---|---|---|
id | yes | Short, unique, lower-case with dashes: acme-cam-oui. Same id as a built-in rule = you're replacing it. |
category | yes | One of ALPR (plate reader / fixed camera), ACOUSTIC, DRONE, TOOL (pentest / RF tool), LE_GEAR (body cam / security gear), TRACKER, TRAFFIC_SENSOR, CAMERA (doorbell / home), FLEET (telematics on buses, ambulances, commercial vehicles — listed, never alerted), VEHICLE, PHONE, WEARABLE, IOT, BEACON, NETWORK, CELL_SITE, UNKNOWN. |
confidence | yes | CONFIRMED only for something that proves the device kind by itself (a maker's own registered OUI, a service UUID nothing else uses). LIKELY for a vendor prefix or company ID where the maker makes several things. POSSIBLE for a name pattern or anything seen in the field but not verified. |
weight | no (10) | Rules are tried highest weight first; the first match wins. 100 for a registry identifier, 60 for a name pattern, 10–30 for a weak hint. |
verified | no (false) | true only when you checked the identifier against the registry (IEEE OUI, Bluetooth SIG) and it really is that company. |
checked | no | Where and when: "ieee-2026-10-18", "field-tampa-2026-11-01". |
note | no | One sentence shown to the user under Why this verdict. Say what the identifier is and what it does not prove. |
regions | no | Country codes the rule applies in, ["NZ","AU"]; empty = everywhere. |
match | yes | The predicate: any of the keys below. All given keys must hold. |
Match keys
| Key | Type | What it matches |
|---|---|---|
ouis | list of "AA:BB:CC" | The first three bytes of a public address: the IEEE MA-L assignment of the maker. Add "publicAddr": true with it, or random Bluetooth addresses will match by accident. Only 6-hex MA-L prefixes; check the registrant. |
publicAddr | true / false | Require a public (or random-static) address, or require a random one. |
manufacturers | list of ints | Bluetooth SIG company identifiers from the manufacturer-data field (decimal: Apple is 76, Tile is 65535… look them up in Assigned Numbers). |
mfgPrefix | list of {"id": 76, "hex": "1219"} | Company ID and the first bytes of its payload, hex. How you tell one of a maker's products from another (an AirTag from AirPods). |
services | list of UUIDs | Advertised service UUIDs, 16-bit ("fd5a") or 128-bit, lower case. The strongest Bluetooth identifier when a maker registered its own. |
sdPrefix | list of {"uuid": "fd5a", "hex": "01"} | Service-data UUID plus the leading bytes of its payload. |
nameRegex | regex | Against the advertised Bluetooth name. Anchor it (^, $) and use (?i) for case-insensitive. A regex that doesn't compile matches nothing rather than everything. |
ssidRegex | regex | Against a Wi-Fi network name. |
sources | list | Limit to BLE, BR_EDR, WIFI, RF (companion radio), CELL. |
Examples
A maker's own OUI — the strongest kind of rule:
{
"id": "example-cam-oui", "category": "ALPR", "confidence": "CONFIRMED", "weight": 100,
"verified": true, "checked": "ieee-2026-11-03",
"note": "Example Cameras Inc.'s registered OUI. Proves the maker, not which model",
"match": { "ouis": ["00:11:22"], "publicAddr": true }
}
A product told apart by its manufacturer-data prefix:
{
"id": "example-tracker-mfg", "category": "TRACKER", "confidence": "LIKELY", "weight": 80,
"note": "Example Co. company ID with the tracker product's payload header",
"match": { "manufacturers": [4660], "mfgPrefix": [{"id": 4660, "hex": "0a01"}], "sources": ["BLE"] }
}
A name pattern seen in the field, honestly weighted:
{
"id": "example-dashcam-ssid", "category": "FLEET", "confidence": "POSSIBLE", "weight": 40,
"verified": false, "checked": "field-2026-11-03",
"note": "In-car video units on the local transit fleet beacon this SSID while parked",
"regions": ["US"],
"match": { "ssidRegex": "(?i)^fleetcam[-_][0-9]{4}$", "sources": ["WIFI"] }
}
Switching a built-in rule off in your pack, because it is wrong or unlawful where you are: give the same id with "category": "UNKNOWN" and "weight": 0, or in a region pack list it under disable_rules.
Region packs: a country in one file
A region pack is the bigger brother: units, the Scanner tab's idle band, extra rules, rules to disable, and the band names a Pi companion uses for your country's allocations (what 476 MHz means in Australia, what 868 MHz means in Europe). The format, the template and the checker are in packs/, and the Packs page lists the ones that exist. Start from TEMPLATE.json, use "status": "testing" until you've driven with it, and open a PR with the regulator's allocation table linked.
Rules of the road
- Everything is a hint. A vendor prefix means this maker's hardware is nearby, never a confirmed device. Write the note that way, and pick the confidence that way.
- Registry first. OUIs from the IEEE registry, company IDs from the Bluetooth SIG. Field observation is welcome with
"verified": falseand acheckedthat says where. - No people. Rules identify kinds of hardware, never a person, a plate, or someone's own phone. We don't take rules that single out an individual's device.
- Follow local law. Detecting some devices is an offence in some places (radar and speed-camera detectors in many countries). A pack must disable such rules where they apply and say so in its notes.
- Receive only. Nothing in Wavelens transmits, and contributions that would need it aren't accepted.
Credit goes in the pack's maintainers and the release notes, under whatever name you give. Questions: wavelensapp@gmail.com.
The fastest contribution is the export
More → Help improve Wavelens where you live, in the app. Two taps, and your town's gear ends up in everyone's pack.
Join the free Android beta The public repo
283 rules as of 6 November · credit in the release notes · nothing is sent until you press send