Releases: jonwilliams84/cli-anything-zigbee2mqtt
Releases · jonwilliams84/cli-anything-zigbee2mqtt
Release list
v0.9.0
- Raw MQTT passthrough: new
mqttcommand group —mqtt publish,
mqtt read,mqtt watch,mqtt topics. z2m's control surface is MQTT:
bridge request/response topics, per-device<name>/setand<name>/get
writes, and retained state dumps. Every command until now went through a
typed wrapper; these four reach anything the wrappers don't model — a
payload key no convenience command covers, a retained dump
(bridge/info,bridge/devices, a device's state topic), an MQTT
wildcard tail, or a future/tweaked bridge API the moment z2m ships it —
without leaving the CLI.mqtt publish <topic> [payload]— topic resolves against the base
topic (lamp/set→zigbee2mqtt/lamp/set; a topic that already starts
with the base topic is used verbatim). A payload string that parses as
JSON is sent as JSON (so the wire is indistinguishable from
device set); any other text is published verbatim; an omitted payload
publishes an empty message.--retainand--qos 0-2set the MQTT
flags; bad values abort before any broker connection. Output
{"topic", "payload", "published", "rc", "retain", "qos"}— gate
scripts onpublished/rclikedevice identify.mqtt read <topic> [--timeout S]— one-shot retained read, JSON-decoded
when possible.payload: nullmeans "nothing ever published on this
topic" — a valid answer, not an error, and the fastest way to read any
retained z2m dump.mqtt watch <filter> [--duration S]— collect every message matching an
MQTT wildcard filter (e.g.sensor1/#,bridge/logging) for
--durationseconds (default 15, Ctrl-C stops early); consumer-callback
errors are logged, never fatal (same contract asbridge watch-events).mqtt topics [--prefix P] [--duration S]— enumerate the topics under
<base>/#: subscribing pulls every retained dump in immediately, so a
short default window (2s) snapshots the whole topic tree with payload
size and a truncated preview per row — topic discovery without knowing
names up front.
- Core functions
normalize_topic,coerce_payload,check_qos,
publish_raw,read_raw,watch_topic,list_topicsin the new
core/mqtt_raw.py; the topic-resolution and payload-coercion helpers are
client-free, like the rest of the introspection helpers. - Bug fix riding along: every watch loop (
mqtt watch,bridge watch-events/watch-logging,device watch) treated--duration 0as
"tail forever" —time.time() + duration if duration else Noneis falsy
on 0 — so a zero-width window hung the CLI. All four now use
duration is not None;--duration 0is a non-blocking drain. - Why it matters: the typed groups are coverage, not a cage. When z2m adds or
changes a bridge API,mqtt publish/mqtt readworks against it the
same minute; and one-off payloads that no convenience command models are a
one-liner instead of a second MQTT tool. Recommended loop:mqtt topics
to discover,mqtt readto inspect,mqtt publishto act,mqtt watch
to confirm. - 64 new tests (40 unit + 24 E2E/workflow); total suite 1055 passed, gate
green (coverage 99.15%, ruff check/format clean, bandit clean).
v0.8.0
device ping: the bridge liveness round trip. Publishes{"id": ...}to
bridge/request/device/ping; z2m performs a real Zigbee read on the
device's basic cluster and reportsdata.successful. This is the third —
and only active — liveness view:device availabilityreads a retained
flag the device last published,device stalereads rawlast_seen, but
onlydevice pingproves the radio link works right now.- IDENT accepts a friendly name or IEEE address; unknown devices abort
before any publish (same asdevice identify). --timeout S(default 15) tunes how long the bridge gets to answer.- A bridge timeout or z2m error surfaces as
successful: falsewith an
errorfield — never a traceback — and--jsonoutput stays parseable,
so scripts can gate on it: the command exits 1 when the device does
not answer. - The flip side, stated in the help text: a sleeping battery device will
not answer a ping even when perfectly healthy — use
device availabilitythere.device ping+device availability/
availability-sweepnow cover both halves of "is it alive?".
- IDENT accepts a friendly name or IEEE address; unknown devices abort
- Core function
pingincore/devices.py. - Why it matters: after a
device availability-sweep --offline-onlyor a
firmware upgrade,device ping <name>is the one-liner that separates
"flag says offline" from "the device really does not answer". - 15 new tests (6 unit + 9 E2E/workflow); total suite 991 passed, gate
green (coverage 99.3%, ruff check/format clean, bandit clean).
v0.7.0
- Lighting convenience layer:
device on / off / toggle / brightness / color / color-temp. Until now the only way to drive a bulb was hand-writing a
device setpayload (device set 'Lounge Lamp' state=ON brightness=128) or
a rawdevice writeon the genOnOff/genLevelCtrl clusters. The new
shortcuts cover the everyday cases with validated arguments:device on <name> [--transition S]/device off/device toggle
publish{"state": "ON" | "OFF" | "TOGGLE"}.device brightness <name> 0-254 [--transition S]— the standard Zigbee
range;255is rejected ("max" is not a settable level on z2m).device color <name> red | #ff8800 | '255,136,0' [--transition S]—
named colors, hex or RGB triples, all normalised to the
{"color": {"r","g","b"}}object z2m expects.device color-temp <name> <mireds> [--kelvin] [--transition S]— mireds
150-500 (lower = cooler);--kelvinconverts (e.g.2700 --kelvin→
color_temp 370).
All six resolve IEEE addresses to friendly names first (same as
device identify), sodevice toggle 0xa4c138…works, and an unknown
device aborts instead of publishing to a nonexistent topic. Payloads are
validated BEFORE any MQTT connection is opened —device brightness <name> 255reports "brightness must be between 0 and 254" rather than a broker
connection error. Output is{"friendly_name", "topic", "published", "rc"}(same shape asdevice identify); likedevice setthere is no
bridge/response, so confirm withdevice state <name>. Room-wide changes
still belong in agroup setgroupcast.
- Core functions
light_payload,set_light,parse_color,
kelvin_to_mireds,check_brightness,check_color_temp,
check_transitionincore/devices.py. - Why it matters:
device find --capability brightness→device brightness <name> 128is now a two-command light-dimming one-liner instead of
property-name archaeology. - 53 new tests (34 unit + 19 E2E/workflow); total suite 976 passed, gate
green (coverage 99.3%, ruff check/format clean, bandit clean).
v0.6.0
device find: multi-criteria search over the paired-device inventory.
Every filter is a local read of the retainedbridge/devicespayload — no
round trip, works against sleeping devices — and they all combine:
--like(substring on friendly_name / IEEE address),--manufacturer,
--model,--vendor,--type(EndDevice / Router / Coordinator),
--power(e.g.battery,mains),--capability(matches any flattened
exposes property, so--capability colorfindscolor_temptoo),
--supported/--unsupportedand--disabled/--enabled.--jsonstays a
pure JSON list even with zero hits; text mode says "No devices match".device identify: trigger the Zigbee Identify effect so a device flashes —
publishes{"identify": {"duration": N}}(or{"identify": {}}) to
<base>/<name>/set, the documented z2m way to reach the Identify cluster.
IEEE addresses are resolved to friendly names first, and an unknown device
aborts instead of publishing to a nonexistent topic.--durationtunes the
flash window; only devices implementing Identify (most bulbs, some sensors)
react.- Why it matters: the two commands compose —
device find --capability brightness→device identify <name>is the
answer to "what's this bulb actually called?" without touching the frontend. - Core functions
search_devicesandidentifyincore/devices.py.
40 new tests (21 unit + 19 E2E/workflow); total suite 923 passed, gate
green (coverage ≥ 80%, ruff check/format clean, bandit clean).
v0.5.0
device battery: network-wide battery audit. One<base>/#subscription
collects every retained device state and joinsbattery(percent),
battery_lowandvoltage(mV) onto thebridge/devicesinventory — no
per-device round trip. Rows sort worst-first (low→unknown→ok,
then percent ascending) so the top row is the device that needs a fresh
cell; mains-powered devices and the coordinator are dropped, and battery
devices that never published state (sleeping sensors) classify as
unknown.--below Nredefines "low" (default 20%),--low-onlydrops
healthy devices,--timeouttunes the inventory read,--durationthe
state-collection window. Core functionsbattery_sweepand
classify_batteryincore/devices.py. 37 new tests (unit + E2E +
workflow); total coverage 99.8%.
v0.4.0
- Updated
test.md. (1 file changed, 24 insertions(+))
v0.3.0
- Updated
test.md. (1 file changed, 9 insertions(+), 7 deletions(-))
v0.2.0
ota check --all: whole-network OTA firmware sweep. One
device/ota_update/checkround trip per device (coordinator and disabled
devices skipped;--include-disabledoverrides), sorted update-available-
first. Each device is classified asupdate_available/up_to_date/
not_supported/unknown/error— a dead device becomes an error row
and never hides the rest of the network's firmware state.--with-update
narrows to devices that actually have pending firmware,--timeouttunes
the per-device round trip. Core functionscheck_all,summarize_check
and_classifyincore/ota.py. 25 new tests (unit + E2E + workflow).
v0.1.1
- README: a Releases section — every merge to
mainis tagged and published as a
semver GitHub Release by the Release workflow,setup.pyholds the version of
record, and this file carries a section per released version.
v0.1.0
What's Changed
- converge: automated change by @jonwilliams84 in #33
- ci: add security scanning (bandit + semgrep + SARIF upload) by @jonwilliams84 in #34
- fix(ci): bandit outputs JSON, convert to SARIF for upload by @jonwilliams84 in #35
- fix(ci): dedup bandit SARIF rules + add pytest stage by @jonwilliams84 in #36
- fix(ci): use --body-file for findings issue by @jonwilliams84 in #38
- fix(ci): cap findings issue body to 200 entries by @jonwilliams84 in #39
- ci: auto-merge converge PRs on green pipeline by @jonwilliams84 in #40
- ci: bump action versions to fix Node 20 deprecation by @jonwilliams84 in #41
- ci: add CodeQL, pip-audit, ruff, coverage, Dependabot by @jonwilliams84 in #42
- ci: bump actions/upload-artifact from 5 to 7 by @dependabot[bot] in #46
- ci: bump actions/setup-python from 5 to 7 by @dependabot[bot] in #45
- ci: bump actions/download-artifact from 5 to 8 by @dependabot[bot] in #43
- converge: automated change by @jonwilliams84 in #47
- converge: automated change by @jonwilliams84 in #48
- converge: automated change by @jonwilliams84 in #49
- converge: automated change by @jonwilliams84 in #50
- converge: automated change by @jonwilliams84 in #51
- converge: automated change by @jonwilliams84 in #52
- converge: automated change by @jonwilliams84 in #53
- converge: automated change by @jonwilliams84 in #54
- converge: automated change by @jonwilliams84 in #55
- converge: automated change by @jonwilliams84 in #56
- converge: automated change by @jonwilliams84 in #57
- converge: automated change by @jonwilliams84 in #58
- converge: automated change by @jonwilliams84 in #59
- converge: automated change by @jonwilliams84 in #60
- converge: automated change by @jonwilliams84 in #61
- fix(ci): stop scanner noise burying real security findings by @jonwilliams84 in #62
- ci: production pipeline - blocking gate + advisory scan by @jonwilliams84 in #63
- fix(ci): stop the findings publisher failing silently by @jonwilliams84 in #64
- converge: automated change by @jonwilliams84 in #65
- fix(ci): actually call publish_findings.py by @jonwilliams84 in #66
- fix(ci): give the advisory ruff scan a wider scope than the gate by @jonwilliams84 in #68
- converge: automated change by @jonwilliams84 in #69
- converge: automated change by @jonwilliams84 in #70
- converge: automated change by @jonwilliams84 in #72
- converge: automated change by @jonwilliams84 in #73
- converge: automated change by @jonwilliams84 in #74
- converge: automated change by @jonwilliams84 in #75
- converge: automated change by @jonwilliams84 in #76
- ci: bump github/codeql-action from 4 to 4.37.3 by @dependabot[bot] in #71
- ci: cut a semver release on every push to main by @jonwilliams84 in #79
New Contributors
- @jonwilliams84 made their first contribution in #33
- @dependabot[bot] made their first contribution in #46
Full Changelog: https://github.com/jonwilliams84/cli-anything-zigbee2mqtt/commits/v0.1.0