Skip to main content

WeatherFlow Tempest (Local UDP)

Local-network integration for the WeatherFlow Tempest weather station. The Tempest hub broadcasts JSON datagrams onto the LAN on UDP port 50222; this driver binds that port and decodes them. No token, no cloud account, no polling — if the hub and the GEM controller share a broadcast domain, the data arrives on its own.

This is the companion to the WeatherFlow Tempest cloud driver. Same hardware, opposite transport.

Which driver do I want?

Local UDP (this page)Cloud REST
Works without internetYesNo
Needs a Personal Access TokenNoYes
Observation latency~1 min, pushed~1 min, polled (plus poll interval)
3-second wind (rapid_wind)YesNo
Lightning / rain-start events as they happenYesNo
Forecast (forecast_*)NoYes
Battery, RSSI, sensor fault bitsYesNo
Works when GEM is on another VLAN or off-siteNoYes
Rain totals for the hour / dayAccumulated locallyFrom WeatherFlow

Neither transport is complete on its own. Running both against one station is the intended full-coverage setup — see Running both drivers below, which has one rule you must follow.

What this driver is for

Read-only. There is nothing to control on a weather station; the value is making live readings available to macros, dashboards, and the weather automation variable — with the local path's lower latency and no dependency on the WeatherFlow cloud being reachable.

The 3-second rapid_wind frame is the thing the cloud path cannot give you. If you have a macro that retracts an awning, closes a louvre roof, or drops a shade on a wind gust, the difference between a 3-second reading and a 1-minute average is the difference between reacting to the gust and reacting to its aftermath.

Prerequisites

ItemNotes
Tempest commissioned in the WeatherFlow appWi-Fi onboarding is mobile-only. This driver does not configure the hub.
Layer-2 reachabilityHub and controller on the same VLAN/subnet, broadcast traffic not filtered. A routed network, a guest SSID, or AP client-isolation all block the frames.
UDP 50222 open inboundOn the controller's host firewall.

There is nothing to authenticate. That is also the security caveat — see Security.

Setup

  1. Confirm the hub is online in the WeatherFlow Tempest mobile app.

  2. Add the device in GEM.

    • Navigate to /admin/devicesAdd Device.
    • Driver: weatherflow_tempest_udp.
    • Leave every optional attribute at its default unless a note below applies. There is no address, port, or credential to enter for a normal install.
  3. Wait about a minute, then run get_devices. The response lists every serial heard on the wire since the device connected:

    {
    "listening": true,
    "port": 50222,
    "bind_address": "0.0.0.0",
    "devices": [
    { "serial_number": "ST-00000512", "hub_sn": "HB-00013030",
    "source_ip": "192.168.1.44", "types": ["obs_st", "rapid_wind", "device_status"],
    "first_seen": 1754600000000, "last_seen": 1754600183000, "bound": false }
    ]
    }

    An empty devices list means the broadcast is not reaching the controller. Go to Troubleshooting — it is a network problem, not a driver problem, and one tcpdump settles it.

  4. Create one zone per sensor.

    • Subsystem: typically weather.
    • zone.address = the serial number from step 3, e.g. ST-00000512. Matching is case-insensitive.
    • This is not the cloud station_id. A zone addressed with a station id will never match a frame.
  5. (Optional) Set elevation_m on the device — the sensor's height above sea level in metres. Without it, sea_level_pressure_mb cannot be computed and is skipped entirely.

  6. (Optional) Set hub_serial if more than one Tempest hub is audible on this LAN — a neighbour's station, or a second building on a flat network — so only yours is accepted.

Addressing

Every frame carries the serial of the hardware that produced it, so that serial is the zone address:

PrefixHardwareFrames it produces
ST-Tempest sensorobs_st, rapid_wind, evt_strike, evt_precip, device_status
AR-Legacy AIR sensorobs_air, evt_strike, device_status
SK-Legacy SKY sensorobs_sky, rapid_wind, evt_precip, device_status
HB-Hubhub_status

A current Tempest station is one ST- sensor plus one HB- hub. Address the ST- serial for weather data. Adding a second zone on the HB- serial is optional and gives you hub diagnostics only (uptime, RSSI, firmware, reset flags) — useful when you are chasing an intermittent station.

Attributes

Device attributes

NameRequiredDefaultDescription
listen_portno50222Port to bind. Fixed in hub firmware; change only for a local relay or test harness.
bind_addressno0.0.0.0Interface to listen on. Pin to one address on a multi-homed controller where only one network carries the hub.
hub_serialnoAccept only frames whose hub_sn matches. Empty accepts every hub heard.
allowed_source_ipsnoComma-separated source addresses permitted to send frames. Empty accepts any LAN source.
units_systemnobothmetric (°C / m/s / mm), imperial (°F / mph / inches), or both. Matches the cloud driver.
elevation_mnoSensor height above sea level, metres. Required for sea_level_pressure_mb.
offline_timeoutno300000Silence in ms before a zone is marked disconnected. Observations arrive every ~1 min, so 5 min tolerates a few dropped frames.
publish_rapid_windnotrueWrite the 3-second wind reading to wind_rapid_*. Turn off to cut ~20 attribute writes per minute per station.

Zone attributes (populated by the driver)

Attribute names match the cloud driver wherever both transports carry the same quantity, so a widget, macro, or weather variable reads identically no matter which driver feeds the zone.

Current observation

AttributeUnitNotes
temperature_c / temperature_f°C / °FAmbient air.
humidity%Relative humidity.
station_pressure_mbmbAs measured at the sensor.
pressure_mb / pressure_in_hgmb / inHgSame value as station_pressure_mb, under the cloud driver's field name.
wind_avg_mps / wind_avg_mphm/s / mphAverage over the report interval.
wind_gust_mps / wind_gust_mphm/s / mphMaximum 3-second sample in the interval.
wind_lull_mps / wind_lull_mphm/s / mphMinimum 3-second sample in the interval.
wind_direction_degdegrees0 = North, 90 = East.
solar_radiation_wm2W/m²Total irradiance.
uv_indexindexUV index, 0–11+.
brightness_luxluxAmbient brightness.
precip_mm_minmmRain in the previous minute.
precip_type / precip_type_labelcode / string0 none, 1 rain, 2 hail, 3 rain + hail.
rainingboolTrue when precip_type > 0 or a rain-start event just landed.
lightning_countcountStrikes in the report interval, as the hub counts them.
lightning_avg_distance_kmkmMean distance over the report interval — see naming.
battery_voltageVSensor battery.
report_interval_minminThe sensor's own reporting cadence.
wind_sample_interval_ssWind sampling cadence.
observation_timestampunix sUseful for staleness checks.

Rapid wind (every ~3 seconds)

AttributeUnitNotes
wind_rapid_mps / wind_rapid_mphm/s / mphInstantaneous wind speed.
wind_rapid_direction_degdegreesInstantaneous direction.
wind_rapid_timestampunix sWhen the sample was taken.

Suppressed entirely when publish_rapid_wind is false.

Events

AttributeUnitNotes
lightning_last_distance_kmkmDistance to the most recent strike, from an evt_strike frame.
lightning_last_energyRaw energy figure for that strike.
lightning_last_timestampunix sWhen it struck.
precip_started_timestampunix sWhen rain was last detected starting.

Accumulated by this driver

AttributeUnitNotes
precip_1hr_mm / precip_1hr_inmm / inRolling one-hour total.
precip_today_mm / precip_today_inmm / inLocal-day total, reset at local midnight.
precip_accum_datestringYYYY-MM-DD day key the daily total belongs to. Written so a restart can tell today's total from yesterday's leftovers.
lightning_count_1hr / lightning_count_3hrcountRolling strike counts from evt_strike frames.

See Accumulated values for what survives a restart.

Derived by this driver

The broadcast carries raw sensor values only. These are computed locally using the standard published formulas — NWS heat index and wind chill, Magnus dew point, Stull wet bulb, ideal-gas air density, hypsometric sea-level reduction. Expect small differences from the cloud driver's values, which come from WeatherFlow's own implementation.

AttributeUnitNotes
dew_point_c / dew_point_f°C / °FMagnus-Tetens.
feels_like_c / feels_like_f°C / °FHeat index when hot, wind chill when cold and windy, air temperature in between.
heat_index_c°CReports air temperature below ~27 °C, where the index is undefined.
wind_chill_c°CReports air temperature above ~10 °C or in calm air, where the index is undefined.
wet_bulb_c°CStull (2011) approximation, ~0.3 °C.
air_density_kgm3kg/m³Humid-air density by partial pressures.
sea_level_pressure_mbmbRequires elevation_m. Skipped entirely when unset.

Diagnostic

AttributeNotes
signal_strengthSensor RSSI in dBm (hub RSSI on an HB- zone).
hub_signal_strengthHub's view of this sensor, dBm.
sensor_status / sensor_status_textFault bitfield and a decoded string (ok, or wind failed, rh failed).
uptime_secondsSince the unit last reset.
firmware_versionFirmware revision reported in the frame.
hub_reset_flagsHB- zones. BOR brownout, PIN pin, POR power, SFT software, WDG watchdog, WWD window watchdog, LPW low power, HRDFLT hard fault.
hub_radio_statusHB- zones. 0 off, 1 on, 3 active, 7 BLE connected.
serial_number / hub_serial_numberEchoed from the frame.

Commands

NameArgsDescription
get_devicesEvery serial heard since connect, with hub, frame types, source IP and last-seen. This is how you find the zone address.
get_status / get_observationaddressDecoded state for one serial — last frame of each type, accumulator state, and which attributes are derived rather than measured.
refreshaddress (optional)Re-publish the last decoded observation onto the zone attributes, for one serial or every bound zone.

There is no get_forecast and no poll command. The broadcast is push-only — there is nothing to ask the hub for, and refresh replays what was last heard rather than fetching anything.

Running both drivers

Both drivers may point at the same physical station, and the usual reason to do it is that neither transport is complete: only the cloud has forecasts, only the broadcast has 3-second wind and hardware diagnostics.

The rule: give each driver its own zone. The addresses differ anyway — 12345 (cloud station id) versus ST-00000512 (hardware serial) — so this happens naturally if you follow each driver's setup steps. Pointing both at one zone makes the slower writer overwrite the faster one on every shared field, and produces attribute history that alternates between two sources.

A common arrangement:

  • Zone weather_outside on the UDP driver — everything live, drives the gust macros and the dashboard.
  • Zone weather_forecast on the cloud driver — the forecast_* attributes only, read by the "will it rain tomorrow" logic.

Accumulated values

The broadcast reports rain only as millimetres in the previous minute (obs_sky index 11, the daily total, is documented as null over UDP). Hourly and daily totals are therefore summed by this driver rather than read off the wire, which has consequences worth knowing:

  • precip_today_mm survives a restart when the stored precip_accum_date still matches today. The driver re-seeds from the zone attribute on the first frame after connect. If the stored day is older, it correctly restarts at zero rather than adopting yesterday's total.
  • precip_1hr_mm restarts at zero after a restart and reaches full accuracy an hour later. There is nothing to re-seed it from.
  • Repeated timestamps are not double-counted. The hub re-sends on retry; a frame whose timestamp has not advanced is reported from existing state rather than added to it.
  • Rolling lightning counts cover strikes GEM heard. lightning_count_1hr and lightning_count_3hr are built from evt_strike frames, so a strike that happened while GEM was down is not in them. lightning_count — the hub's own count for the report interval — is unaffected.

A note on lightning naming

lightning_avg_distance_km (from an observation) is the mean distance over the report interval. The cloud driver's lightning_last_distance_km is the distance to the last strike. They are different quantities, so this driver uses different names and publishes lightning_last_distance_km only from an evt_strike frame, where it really is the last strike.

Security

A broadcast listener is unauthenticated by construction. Any host on the LAN can send a well-formed frame to UDP 50222 and this driver will believe it, the same as it believes the hub. If weather readings drive physical actuation — retracting an awning, closing a roof — treat that as part of the threat model:

  • Set allowed_source_ips to the hub's address. Give the hub a DHCP reservation first so it does not move.
  • Set hub_serial so frames from any other hub are rejected even from an allowed address.
  • Both filters are off by default, because on a normal trusted home LAN they are friction for no gain. Turn them on where the LAN is not trusted.

Frames larger than 8 KB, malformed JSON, and frames missing serial_number or type are dropped before parsing goes anywhere. Rejected sources are logged at most once per minute per reason, so a persistent bad broadcaster cannot flood the log.

Known limitations

  • Broadcast only. If GEM is on another VLAN, behind a router, or on an SSID with client isolation, nothing arrives and there is no error to see — get_devices simply stays empty. Use the cloud driver on those networks.
  • No forecast. Better Forecast is a cloud product with no broadcast equivalent.
  • Derived fields are approximations. Close to WeatherFlow's, not identical.
  • sea_level_pressure_mb needs elevation_m and is skipped without it.
  • No pressure_trend. The cloud computes it over a longer window than this driver keeps.
  • Rapid wind is a lot of writes. ~20 attribute writes per minute per station. Enable history on wind_rapid_* only if you genuinely want 3-second trending — it is a very large number of rows.

Troubleshooting

SymptomWhat to check
get_devices returns an empty list, device never connectsFrames are not reaching the host. Run tcpdump -i any udp port 50222 on the controller: no packets means the network (VLAN, client isolation, host firewall); packets means the driver. Confirm hub and controller are on the same subnet.
Device connects but a zone stays disconnectedThe zone address does not match a serial on the wire. Run get_devices and copy the serial exactly — a zone addressed with the cloud station_id will never match.
sea_level_pressure_mb never appearselevation_m is unset on the device.
precip_today_mm reads zero after a restart during rainExpected when the stored accumulation was for a previous day or the attribute was cleared. It resumes counting from the next rain frame.
bind EADDRINUSE in the logAnother process holds 50222 without SO_REUSEADDR. The driver retries every 5 s and recovers once that listener releases the port.
Attribute history is enormousRapid wind writes every ~3 s. Disable history on wind_rapid_* in /admin/attributes, or set publish_rapid_wind to false.
Readings look wrong or impossibleSomething else on the LAN may be broadcasting frames. Set allowed_source_ips to the hub's IP and hub_serial to its serial.

See also

  • WeatherFlow Tempest — the cloud REST driver for the same hardware. The one to use for forecasts, or when GEM is not on the hub's LAN.
  • Automation variables — the weather variable can be pointed at a Tempest zone instead of an external API.