106 lines
3.3 KiB
Python
106 lines
3.3 KiB
Python
"""
|
|
Copy this file, rename it, change the id - you have a new dashboard panel.
|
|
|
|
Every control below shows one widget type. Delete what you do not need.
|
|
After editing, press "Reload extensions" in the Extensions tab.
|
|
|
|
Files starting with an underscore are skipped by the loader, so this template
|
|
never appears in the UI until you copy it to a normal name.
|
|
"""
|
|
|
|
from backend.plugin_api import Plugin
|
|
|
|
plugin = Plugin(
|
|
id="template",
|
|
name="Template",
|
|
description="Every control type, as a starting point.",
|
|
icon="\U0001F9E9",
|
|
order=900,
|
|
)
|
|
|
|
|
|
# A button. style: default | primary | warn | danger
|
|
@plugin.action("hello", label="Say hello", style="primary", icon="\U0001F44B")
|
|
async def hello(ctx):
|
|
await ctx.bridge.speak("Hello from a dashboard extension")
|
|
return "Spoke"
|
|
|
|
|
|
# A destructive button asks for confirmation first.
|
|
@plugin.action("reset", label="Reset counter", style="danger",
|
|
confirm="Reset the stored counter to zero?")
|
|
async def reset(ctx):
|
|
ctx.storage["count"] = 0
|
|
await ctx.push("count", 0)
|
|
return "Counter reset"
|
|
|
|
|
|
# A slider. live=True fires continuously while dragging; leave it False to fire
|
|
# only on release, which is what you want for anything that moves the robot.
|
|
@plugin.slider("speed", label="Cruise speed", min=0.0, max=0.6,
|
|
step=0.05, default=0.2, unit="m/s",
|
|
help="Applied when the routine below starts.")
|
|
async def speed(ctx, value):
|
|
ctx.storage["speed"] = value
|
|
return f"Speed set to {value:.2f} m/s"
|
|
|
|
|
|
@plugin.toggle("verbose", label="Verbose logging", default=False)
|
|
async def verbose(ctx, value):
|
|
ctx.storage["verbose"] = value
|
|
return "Verbose on" if value else "Verbose off"
|
|
|
|
|
|
@plugin.select("gait", label="Gait", default="normal", options=[
|
|
{"value": "slow", "label": "Slow and steady"},
|
|
{"value": "normal", "label": "Normal"},
|
|
{"value": "brisk", "label": "Brisk"},
|
|
])
|
|
async def gait(ctx, value):
|
|
ctx.storage["gait"] = value
|
|
return f"Gait: {value}"
|
|
|
|
|
|
@plugin.text("announce", label="Announcement", placeholder="Type something to say",
|
|
submit_label="Speak")
|
|
async def announce(ctx, value):
|
|
result = await ctx.bridge.speak(value, priority=6)
|
|
return result.message
|
|
|
|
|
|
@plugin.number("repeats", label="Repeat count", min=1, max=10, step=1, default=3)
|
|
async def repeats(ctx, value):
|
|
ctx.storage["repeats"] = int(value)
|
|
return f"Will repeat {int(value)}x"
|
|
|
|
|
|
@plugin.color("tint", label="Strip colour", default="#3987e5")
|
|
async def tint(ctx, value):
|
|
value = value.lstrip("#")
|
|
r, g, b = (int(value[i:i + 2], 16) for i in (0, 2, 4))
|
|
result = await ctx.bridge.set_led(mode=0, r=r, g=g, b=b)
|
|
return result.message
|
|
|
|
|
|
# Declared readouts appear as stat tiles. chart=True adds a sparkline fed by
|
|
# ctx.record().
|
|
plugin.readout("count", "Ticks", chart=True, precision=0)
|
|
plugin.readout("battery", "Battery seen", unit="%", precision=1)
|
|
|
|
|
|
@plugin.on_start
|
|
async def start(ctx):
|
|
ctx.storage.setdefault("count", 0)
|
|
await ctx.log("Template extension loaded")
|
|
|
|
|
|
@plugin.on_tick(interval=2.0)
|
|
async def tick(ctx):
|
|
ctx.storage["count"] = ctx.storage.get("count", 0) + 1
|
|
await ctx.push("count", ctx.storage["count"])
|
|
ctx.record("count", ctx.storage["count"])
|
|
|
|
battery = ctx.state.battery_pct
|
|
if battery is not None:
|
|
await ctx.push("battery", round(battery, 1))
|