gmg-local
Local UDP client for Green Mountain Grills Wi-Fi pellet grills.
No cloud account, no vendor API, no internet. It talks to the grill on your LAN and nothing else.
Not on PyPI yet - built and tested, awaiting a decision to publish. Origin and attribution: NOTICE.md.
from gmg_local import discover
for grill in discover():
state = grill.status()
print(grill.serial_number, state["temp"], "F")
Why this exists
Every public Python implementation of this protocol froze on 2023-01-28. The most-installed Home Assistant integration for these grills reads grill temperature from a single byte, so a 350 °F grill reports as 94 °F - the one number the device exists to tell you.
u16(94, 1) == 350 # not 94
Install
pip install -e . # from a clone; not on PyPI yet
Requires Python 3.11+. No dependencies.
API
discover(timeout=1, ip_bind_address="0.0.0.0", target=None) |
Broadcast for grills. Pass target to unicast across VLANs, where broadcast dies at the L2 edge. |
Grill.status(retries=5) |
One poll, decoded. Retries on silence and on junk. |
Grill.firmware(retries=5) |
The firmware string (UN!), verbatim - e.g. UNJB02SUF0_2.3. Same retry contract as status(). |
model_for(firmware) |
Pure. "Jim Bowie" for a JB firmware prefix, None for anything unrecognised. |
Grill.set_temp(f) / set_temp_probe(f, n) |
Targets, in Fahrenheit. |
Grill.power_on() / power_on_cool() / power_off() |
Cold smoke is power_on_cool. |
parse_status(frame) |
Pure. No sockets, no clock - testable without a grill. |
poll_interval_for(state) |
10s while cooking, 60s while off. |
What the status frame contains
A healthy grill answers UR001! with 52 bytes; this library decodes through
byte 33.
| offset | field | notes |
|---|---|---|
| 2, 4, 6 | grill temp, probe 1, setpoint | 16-bit LE pairs |
| 8 | API version | |
| 16, 18 | probe 2 temp and setpoint | |
| 24-27 | warn state | 32-bit LE |
| 28 | probe 1 setpoint | |
| 30 | power state | 0 Off, 1 On, 2 Fan, 3 Cold Smoke |
| 32 | fire state | 0 Default, 1 Off, 2 Startup, 3 Running, 4 Cool Down, 5 Fail, 198 Cold Smoke |
| 33 | fire state progress | see below |
| 34-51 | undecoded | gmg 0.0.4 labels 48 and 50 as pellet alarms - unverified |
Byte 33
Emitted as fireStatePercentage. GMG's own cloud API names this field
fireStateProgress, paired with fireState.
It is a four-step progress marker through whatever fire state the grill is
currently in. Across a full instrumented cycle it ran 25 → 50 → 75 → 100
during Startup, hitting 100 at the exact second the grill switched to Running,
then 75 → 50 → 0 through Cool Down, hitting 0 as the fan stopped.
Two readings are ruled out by that data:
- Not a startup-stage index. A stage counter does not run backwards.
- Not a pellet-hopper level. A hopper does not fill during ignition and
empty when the fan stops. One public implementation labels it
hopper_pctand ships it as a pellet gauge; it reads 100% on a hot grill and 0% on a cold one.
What each 25% step physically means is unknown, and is not in the packet. Display the step number; do not invent labels.
Things the hardware does that will surprise you
- Probe writes are silently discarded while the grill is off. Write 203, read back 0. No error, no acknowledgement.
- The grill serves one client at a time. Concurrent conversations lose messages, so all I/O here is serialised behind a lock.
- Short datagrams happen. They are a transport artifact, not data. Parsing one either raises or invents fields; this library retries instead.
- Temperature keeps rising after the fire is cut - roughly 18 °F over a minute in one observed shutdown.
Tests
pip install -e ".[dev]" && pytest
The fixtures are reconstructed - real decoded observations re-encoded at the documented offsets - not captured frames. They prove the parser is self-consistent with observed behaviour, not that the offsets are correct. Offsets are corroborated across four independent implementations.
Preserving a genuine 52-byte capture is the prerequisite for decoding bytes 34-51, and should replace these fixtures when one exists.
Credits
See NOTICE.md. Original integration by jwhitby91; protocol
cross-checked against brandenc40/green-mountain-grill and Christopher
McKay's gmg.
Release files for gmg-local 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gmg_local-0.3.0.tar.gz | 21.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gmg_local-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.9 kB
Release files / gmg_local-0.3.0.tar.gz
| Download URL | gmg_local-0.3.0.tar.gz |
|---|---|
| Size | 21.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6c366fa6bcceffebc6a405a89c085c8ec169122079c953bd3b863798d8728dee
|
|
BLAKE2b-256 checksum How to use checksums |
5f2bc0dc24cbc9b4ff2bd7143990d69eed7250cb2c575bf77f384167e29d86f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / gmg_local-0.3.0-py3-none-any.whl
| Download URL | gmg_local-0.3.0-py3-none-any.whl |
|---|---|
| Size | 13.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c6e06d24c6c3b4a1b5234b025e85d4636acefa91a9d73bb9d97b58987869e2a0
|
|
BLAKE2b-256 checksum How to use checksums |
e8a9eab60dc5419b5416367d057c7c8135045935739fe7bcdac76314839df970
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency log