TPMSTempNet leverages parked vehicle TPMS (Tire Pressure Monitoring System) sensors via RTL-SDR to infer local ambient temperature. It routes decoded TPMS data through a custom WeeWX driver, sanitizes and filters the data to isolate stable cold-ambient readings, and can optionally broadcast this Urban Heat Island telemetry directly to Nostr relays (relaying.earth compliant) or seamlessly pass the metrics to standard weather networks like Weather Underground and CWOP.
- rtl_433 installed and accessible in your system path.
- WeeWX (v4 or v5) installed and running.
- Nostr publisher: No additional third-party Python packages are required.
nostr_publisher.pycontains its own pure-Python secp256k1/BIP-340 cryptography and secure, TLS v1.2+ enforced WebSocket publishing implementation.
Save the provided Python scripts into your WeeWX user directory:
- For WeeWX v5 (pip install):
~/weewx-data/bin/user/ - For WeeWX v4 (legacy install):
/usr/share/weewx/user/or/home/weewx/bin/user/
Place sdr.py and nostr_publisher.py inside this directory. Additionally, keep the tools/ directory alongside your setup for standalone Nostr tools.
Open your weewx.conf configuration file and make the following changes:
A. Enable the SDR Driver Change your station type to SDR:
station_type = SDRAdd the [SDR] block. Note: Adjust frequency flags (-f) for your region.
[SDR]
driver = user.sdr
# --- Optional Override ---
# The driver uses a built-in optimized DEFAULT_CMD automatically.
# Uncomment and specify 'cmd' only if you need to override paths, devices, frequencies, hop intervals, sample rates, gains, or exclude specific vehicles:
# cmd = /usr/local/bin/rtl_433 -d driver=sdrplay -M utc -F json -f 315M -f 433.92M -H 15 -s 1024k -g 0 -R 0 ... -R -999 ...
[[sensor_map]]
outTemp = temperature.sane.UniversalTPMSPacket
# --- Optional TPMS Filter Tuning ---
# The driver uses these built-in defaults automatically.
# Uncomment and adjust these values only if you need to change the filter tolerances:
# max_age = 3600 # Seconds before a stale sensor is dropped
# quarantine = 7200 # Seconds to ignore a sensor showing impossible rate spikes
# dwell_sec = 600 # Wait time before a new sensor is trusted
# min_temp = 0.0 # Minimum valid physical temperature bound in Celsius
# max_temp = 46.0 # Maximum valid physical temperature bound in CelsiusPrivacy Note: This driver strips and anonymizes TPMS hardware IDs. No vehicle-identifying data is retained or transmitted; only sanitized temperature metrics are processed.
B. Add the Nostr Publisher & Map Registration (Optional)
(Skip this step if you do not wish to broadcast to Nostr.)
Under [StdRESTful], add the [[Nostr]] block. On its first startup, if private_key is left blank, the publisher will automatically generate a secure Nostr keypair and print to the terminal.
[[Nostr]]
# Enable the urban heat island broadcast
enable = true
# Target observation
target_observation = outTemp
# Geohash length (7 = ~150m x 150m resolution for street-level mapping)
geohash_precision = 7
# Hexadecimal Private Key (auto-generated on first run if left blank)
private_key = ''
# Target Relays (leave blank to use built-in default relay network)
relays = C. Enable the Nostr Publisher in the Engine (Optional)
(Skip this step if you do not wish to broadcast to Nostr.)
In the [Engine] -> [[Services]] section:
Append user.nostr_publisher.Nostr to your process_services line.
process_services = ..., user.nostr_publisher.NostrRestart the WeeWX daemon to apply changes. If Nostr is enabled, this will also broadcast your station profile (Kind 16158) to map frontends like relaying.earth:
sudo systemctl restart weewxBecause TPMSTempNet strictly anonymizes all vehicle data, specific TPMS models and hardware IDs are never written to your database or system logs.
To watch local tire traffic in real-time, simply run weewxd directly in your foreground terminal. The driver intercepts and force-prints the raw hardware data to your active window alongside the standard loop packets:
[TPMS INTERCEPT] Model: Ford | ID: bhZw4ts6 | Temp: 25.0°C
Privacy Note: This terminal output is volatile. It physically bypasses WeeWX's internal loggers and vanishes as soon as you close the session. Once WeeWX is deployed as a background daemon, this data becomes entirely invisible.
TPMSTempNet includes lightweight diagnostic tools inside the tools/ directory to manage and inspect your live profile or telemetry events directly from the command line across any supported relay.
While the plugin automatically registers your station on startup, you can use this tool to manually force an immediate profile update to the Nostr network. This is useful if you just updated your station's name or location in weewx.conf and want to push the changes to the map without having to restart the entire WeeWX daemon.
To manually broadcast your station profile, run the script and point it directly to your WeeWX configuration file:
python3 tools/register_station.py /path/to/weewx.confThe script will securely parse your coordinates and private key, generate a compliant Kind 16158 profile event, and output the live acceptance status from each relay.
To verify that your station profile or telemetry events are successfully indexed on the network, pass the event hex ID to the diagnostic script:
python3 tools/fetch_event.py <EVENT_ID_HEX>You can optionally target a specific relay using the --relay flag:
python3 tools/fetch_event.py <EVENT_ID_HEX> --relay wss://nos.lolTo remove obsolete or outdated station profiles or telemetry events from the network, you can use the deletion tool. This script signs and broadcasts a standard Nostr Kind 5 deletion event containing the target event IDs.
To run the deletion tool interactively:
python3 tools/direct_delete_nostr.pyThe script will prompt you for your private key hex and the target Event IDs you wish to purge, then broadcast the deletion request across your configured relays.
As of early November 2026, all major TPMS models supported by rtl_433 are mapped under the UniversalTPMSPacket class and included in the weewx.conf [SDR] block. If rtl_433 adds support for new vehicle protocols in the future, you can easily bridge them in two steps:
- Add the new base identifier string to the
supported_tpms_models = []array at the bottom of that class insdr.py. - Append the new protocol's ID number to the
DEFAULT_CMDstring near the top ofsdr.py(below the imports), using the-R <ID>flag (e.g.,-R 999) or override it locally via thecmdoption in yourweewx.conf.
The built-in median-centered clustering and outlier filters handle most microclimate variations automatically, but vehicles parked inside insulated garages, carports, or subterranean structures can sometimes report temperatures that skew mildly inconsistent with open-air ambient readings.
If you notice a specific vehicle causing anomalies during your terminal intercept checks (when running weewxd in the foreground), you can temporarily exclude its sensor protocol:
- Locate the
DEFAULT_CMDdefinition near the top ofsdr.pybelow the imports (or add a customcmdoverride line inside your[SDR]block inweewx.conf). - Find the protocol flag associated with that vehicle (refer to the rtl_433 supported devices list for the numeric legend).
- Temporarily negate (e.g.,
-R -999) or remove its-R 999entry from thecmdstring. Tip: It should be easier to prefix the ID with a minus sign, rather than removing that specific-Rentry and trying to piece it back together later.
Warning on Protocol Exclusions: Protocol IDs in
rtl_433(e.g.,-R 60) apply globally to an entire manufacturer protocol family, not just your specific vehicle make or model. If you drop a protocol flag, you will stop receiving telemetry from any vehicle in range using that protocol. Only use this method if you are certain no other neighboring vehicles rely on that same protocol ID.
- Gateway vs. Sensor: The RTL-SDR dongle functions strictly as a local RF receiving gateway over a
usb/ethernetbackhaul, while the telemetry data source is globally declared as Multi-TPMS to reflect the asphalt sensor array. - Standards Compliance:
- Kind 16158: Automatically manages map registration on startup using explicit tags (
name,description,geohash,connectivity,sensor,sensor_status) with an empty content payload. - Kind 4223: Transmits periodic temperature telemetry payloads utilizing standard
["temp", value, "Multi-TPMS"]tags.
- Kind 16158: Automatically manages map registration on startup using explicit tags (
To infer stable outdoor ambient temperatures from parked vehicle TPMS sensors while avoiding engine heat, direct sunlight, and erratic microclimate variations (such as insulated garages or indoor storage), the driver implements a multi-layer filtering and aggregation state machine:
- Ingestion, Burst Debouncing, & Rate-of-Change Validation: Every incoming sensor reading first evaluates a 60-second change-gated burst throttle to instantly drop redundant duplicate frames from the same sensor ID while allowing real temperature steps to pass through without delay. It then checks the elapsed time since the sensor's last accepted transmission against a strict
max_ratelimit. It includes an integer quantization bypass that safely forgives standard 1°C steps, alongside a hopping gap tolerance floor (Δ ≥ 3.0°C) that prevents intermittent SDR frequency-hopping gaps from triggering false quarantines on normal cooling steps, while aggressively isolating actual driving or warm-up spikes. - The Filter Pool: Before a sensor's temperature is considered for the ambient baseline, it must pass four strict criteria:
- Max Age: Sensors that haven't transmitted within
max_ageseconds are purged from memory entirely. - Quarantine Status: Quarantined sensors are ignored.
- Dwell Time: New sensors must survive in the system longer than
dwell_secbefore their data is trusted, preventing fresh, uncalibrated tires from skewing the baseline. - Physical Bounds: The temperature must fall strictly between
min_tempandmax_temp(default 0°C to 46°C).
- Max Age: Sensors that haven't transmitted within
- Median-Centered Cluster Consensus & Small-Pool Divergence Check: Once the pool of valid readings is compiled, the driver checks for small-pool states: if exactly two sensors are active and they diverge by more than 4.0°C, the higher reading is treated as a slow-cooling tire outlier and dropped in favor of the true ambient baseline. Otherwise, it establishes a gravitational center using the statistical median of all valid candidates with a tight cluster window (±5.0°C) to reject dual-extremity anomalies (high friction-heated driving tires and low insulated garage/indoor microclimate sensors). The final ambient baseline is then computed from the remaining consensus cluster.
The SDR driver in this repository (sdr.py) is a heavily modified fork of the original weewx-sdr driver created by Matthew Wall. It has been customized to embed a native TPMS state machine and median-centered cluster consensus filter directly into the packet generation loop, and define a single universal TPMS packet class.
This project is distributed under the GNU General Public License v3.0 (GPL-3.0).