Help for ocpp.csms.view_charger_status
Sample CLI
gway ocpp.csms view-charger-status
Full Code
def view_charger_status(*, action=None, charger_id=None, **_):
"""
Card-based OCPP dashboard: summary of all charger connections.
"""
if request.method == "POST":
action = request.forms.get("action")
charger_id = request.forms.get("charger_id")
if action and charger_id:
try:
dispatch_action(charger_id, action)
except Exception as e:
gw.error(f"Failed to dispatch action {action} to {charger_id}: {e}")
return redirect(request.fullpath or "/ocpp/charger-status")
all_chargers = set(_active_cons) | set(_transactions)
html = ["<h1>OCPP Status Dashboard</h1>"]
# --- Show abnormal statuses if present ---
if _abnormal_status:
html.append(
'<div style="color:#fff;background:#b22;padding:12px;font-weight:bold;margin-bottom:18px">'
"⚠️ Abnormal Charger Status Detected:<ul style='margin:0'>"
)
for cid, err in sorted(_abnormal_status.items()):
status = err.get("status", "")
error_code = err.get("errorCode", "")
info = err.get("info", "")
ts = err.get("timestamp", "")
msg = f"<b>{cid}</b>: {status}/{error_code}"
if info: msg += f" ({info})"
if ts: msg += f" <span style='font-size:0.9em;color:#eee'>@{ts}</span>"
html.append(f"<li>{msg}</li>")
html.append("</ul></div>")
if not all_chargers:
html.append('<p><em>No chargers connected or transactions seen yet.</em></p>')
else:
html.append('<div class="ocpp-dashboard">')
for cid in sorted(all_chargers):
ws_live = cid in _active_cons
tx = _transactions.get(cid)
connected = '🟢' if ws_live else '🔴'
tx_id = tx.get("transactionId") if tx else '-'
meter_start = tx.get("meterStart") if tx else '-'
latest = (
tx.get("meterStop")
if tx and tx.get("meterStop") is not None
else (tx["MeterValues"][-1].get("meter") if tx and tx.get("MeterValues") else 'None')
)
power = power_consumed(tx)
status = "Closed" if tx and tx.get("syncStop") else "Open" if tx else '-'
# Heartbeat
raw_hb = _latest_heartbeat.get(cid)
if raw_hb:
try:
from datetime import datetime, timezone
dt = datetime.fromisoformat(raw_hb.replace("Z", "+00:00")).astimezone()
latest_hb = dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
latest_hb = raw_hb
else:
latest_hb = "-"
html.append('<div class="charger-card">')
html.append(f'''
<div class="charger-header">
<span class="charger-id">{cid}</span>
<span class="charger-status">{connected}</span>
</div>
<div class="charger-details-row">
<label>Txn ID:</label> <span>{tx_id}</span>
<label>Start:</label> <span>{meter_start}</span>
<label>Latest:</label> <span>{latest}</span>
</div>
<div class="charger-details-row">
<label>kWh:</label> <span>{power}</span>
<label>Status:</label> <span>{status}</span>
<label>Last HB:</label> <span>{latest_hb}</span>
</div>
<form method="post" action="" class="charger-action-row">
<input type="hidden" name="charger_id" value="{cid}">
<select name="action" id="action-{cid}" aria-label="Action">
<option value="remote_stop">Stop</option>
<option value="reset_soft">Soft Reset</option>
<option value="reset_hard">Hard Reset</option>
<option value="disconnect">Disconnect</option>
</select>
<button type="submit" name="do" value="send">Send</button>
<button type="submit" name="do" value="details" class="details-btn" data-target="details-{cid}">Details</button>
<button type="submit" name="do" value="graph" class="graph-btn">Graph</button>
</form>
<div id="details-{cid}" class="charger-details-panel hidden">
<pre>{json.dumps(tx or {}, indent=2)}</pre>
</div>
''')
html.append('</div>')
html.append('</div>') # end .ocpp-dashboard
# WebSocket URL bar
ws_url = gw.build_ws_url()
html.append(f"""
<div class="ocpp-wsbar">
<input type="text" id="ocpp-ws-url" value="{ws_url}" readonly
style="flex:1;font-family:monospace;font-size:1em;
padding:10px 6px;background:#222;color:#fff;
border:1px solid #333;border-radius:5px;min-width:160px;max-width:530px;"/>
<button id="copy-ws-url-btn"
style="padding:6px 16px;font-size:1em;border-radius:5px;
border:1px solid #444;background:#444;color:#fff;cursor:pointer">
Copy
</button>
</div>
""")
return "".join(html)