Python client: port stream-alarms and acknowledge-alarm

Adds the session-less alarm CLI subcommands to mxgw-py. stream-alarms reads a
bounded slice of the gateway's central alarm feed (--filter-prefix,
--max-messages, --timeout, --json; aggregate `{messages: [...]}`);
acknowledge-alarm is a unary ack (--reference required, --comment, --operator).
GatewayClient.stream_alarms joins query_active_alarms via a
_canceling_alarm_feed_iterator helper mirroring the existing
_canceling_active_alarms_iterator pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-24 06:45:54 -04:00
parent 7de4efeb02
commit 828e3e6cf6
3 changed files with 146 additions and 0 deletions
@@ -172,6 +172,28 @@ class GatewayClient:
call = self.raw_stub.QueryActiveAlarms(request, **kwargs)
return _canceling_active_alarms_iterator(call)
def stream_alarms(
self,
request: pb.StreamAlarmsRequest,
*,
metadata: Sequence[tuple[str, str]] | None = None,
) -> AsyncIterator[pb.AlarmFeedMessage]:
"""Attach to the gateway's central alarm feed.
The stream opens with one ``AlarmFeedMessage`` per currently-active
alarm (the ConditionRefresh snapshot), then a single
``snapshot_complete``, then a ``transition`` for every subsequent
raise / acknowledge / clear. Served by the gateway's always-on alarm
monitor — no worker session is opened — so any number of clients may
attach. Optionally scoped by alarm-reference prefix
(``request.alarm_filter_prefix``).
"""
kwargs: dict[str, Any] = {"metadata": merge_metadata(self.options.api_key, metadata)}
if self.options.stream_timeout is not None:
kwargs["timeout"] = self.options.stream_timeout
call = self.raw_stub.StreamAlarms(request, **kwargs)
return _canceling_alarm_feed_iterator(call)
async def _unary(
self,
operation: str,
@@ -223,3 +245,15 @@ async def _canceling_active_alarms_iterator(call: Any) -> AsyncIterator[pb.Activ
cancel = getattr(call, "cancel", None)
if cancel is not None:
cancel()
async def _canceling_alarm_feed_iterator(call: Any) -> AsyncIterator[pb.AlarmFeedMessage]:
try:
async for message in call:
yield message
except grpc.RpcError as error:
raise map_rpc_error("stream alarms", error) from error
finally:
cancel = getattr(call, "cancel", None)
if cancel is not None:
cancel()