Cookwala / SDK and scenarios / 015

The five presets of the home-page demo, from the SDK

The dry run on the home page uses five device presets. They are exported to examples/capabilities so the hub, the CLI and every SDK see the same devices; this scenario reproduces the demo for shakshuka.

Audience: developer · Goal: Robots for people · Level: beginner

Read first: /docs/QUICKSTART/ /docs/CORE/

Steps

  1. The presets carry the vendor name "Cookwala demo presets"; the basic hob robot has one sensor, its camera. getDevices

  2. Counter arm: cuts under its camera, cracks the eggs; the person heats and sautes. dryRun

  3. Hob robot without thermometers: heating is a logged estimate. dryRun

  4. Hob robot with thermometers: heating is verified by the pan thermometer. dryRun

  5. Smart oven: it lists cw.op.heat, so the dry run gives it the pan-heating step as an estimate and leaves the rest to the person. The dry run matches operations, not equipment; the recipe's equipment list is a separate check. dryRun

  6. Fryer robot: same as the hob robot for this dish, thirteen steps by the device. dryRun

Outcome

You can reproduce any home-page result from code, and you have seen one limit of the dry run worth knowing: it checks operations and sensors, not whether the device owns the pan. Next: scenario 020 checks a temperature trace against an envelope.

Command line

cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-arm.json --human-present
cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-hob-robot-basic.json --human-present
cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-hob-robot.json --human-present
cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-oven.json --human-present
cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-fryer-robot.json --human-present

Recorded output

Status: ok

[1] getDevices: {"devices": {"demo-arm": {"cookwala": "0.1.0", "actor": {"kind": "robot", "id": "demo:arm", "vendor": "Cookwala demo presets", "model": "Counter robot arm (cuts, cracks, mixes, moves)"}, "roles": ["executor"], "location": {"zone": "kitchen", "mobile": false}, "capabilities": {"ops": [{"op": "cw.op.cut"}, {"op": "cw.op.crack"}, {"op": "cw.op.transfer"}, {"op": "cw.op.mix"}, {"op": "cw.op.whisk"}, {"op": "cw.op.wash"}, {"op": "cw.op.drain"}, {"op": "cw.op.crush"}, {"op": "cw.op.grate"}, {"op": "cw.op.layer"}, {"op": "cw.op.garnish"}, {"op": "cw.op.form"}], "sensors": [{"sensor": "cw.sense.vision…
[2] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": "human", "verifiedBy": "human"}, {"node": "n6", "op": "cw.op.saute", "by": "human", "verifiedBy": "human"}, {"node": "n7", "op": "cw.op.simmer", "b…
[3] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "device", "verifiedBy": "model", "rung": "model"}, {"node": "n5", "op": "cw.op.saute", "by": "device", "verifiedBy": "model", "rung": "model"}, {"node": "n6", "op": "cw.op.saute", "by": "device", "verifiedBy": "model", "ru…
[4] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.pan_surface_temp"}, {"node": "n5", "op": "cw.op.saute", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.pan_surface_temp"}, {"node": "n6", "op": "cw.op.saute", …
[5] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.cut", "by": "human", "verifiedBy": "human"}, {"node": "n2", "op": "cw.op.cut", "by": "human", "verifiedBy": "human"}, {"node": "n3", "op": "cw.op.cut", "by": "human", "verifiedBy": "human"}, {"node": "n4", "op": "cw.op.heat", "by": "device", "verifiedBy": "model", "rung": "model"}, {"node": "n5", "op": "cw.op.saute", "by": "human", "verifiedBy": "human"}, {"node": "n6", "op": "cw.op.saute", "by": "human", "verifiedBy": "human"}, {"node": "n7", "op": "cw.op.simmer", "by": "human", "verifiedBy": "human"}, {"node": "n8", "op": "cw.op.seas…
[6] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.pan_surface_temp"}, {"node": "n5", "op": "cw.op.saute", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.pan_surface_temp"}, {"node": "n6", "op": "cw.op.saute", …

Code

Run: python scenarios/out/015-browser-demo-presets/python.py

# Scenario 015: The five presets of the home-page demo, from the SDK
# The dry run on the home page uses five device presets. They are exported to examples/capabilities so the hub, the CLI and every SDK see the same devices; this scenario reproduces the demo for shakshuka.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem

def load(path):
    with open(path, encoding="utf-8") as f:
        return json.load(f)

def pick(obj, path):
    for part in path.replace("]", "").replace("[", ".").split("."):
        obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
    return obj

c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))


# Step 1: The presets carry the vendor name "Cookwala demo presets"; the basic hob robot has one sensor, its camera.
devices = c.get_devices()
assert pick(devices, "devices.demo-arm.actor.vendor") == "Cookwala demo presets", pick(devices, "devices.demo-arm.actor.vendor")
print("ok", "devices.demo-arm.actor.vendor", "=", "Cookwala demo presets")
assert pick(devices, "devices.demo-hob-robot-basic.capabilities.sensors.length") == 1, pick(devices, "devices.demo-hob-robot-basic.capabilities.sensors.length")
print("ok", "devices.demo-hob-robot-basic.capabilities.sensors.length", "=", 1)

# Step 2: Counter arm: cuts under its camera, cracks the eggs; the person heats and sautes.
arm = c.dry_run(recipe_id="shakshuka", device_id="demo-arm", human_present=True)
assert pick(arm, "state") == "accepted", pick(arm, "state")
print("ok", "state", "=", "accepted")
assert pick(arm, "plan[0].by") == "device", pick(arm, "plan[0].by")
print("ok", "plan[0].by", "=", "device")
assert pick(arm, "plan[4].op") == "cw.op.saute", pick(arm, "plan[4].op")
print("ok", "plan[4].op", "=", "cw.op.saute")
assert pick(arm, "plan[4].by") == "human", pick(arm, "plan[4].by")
print("ok", "plan[4].by", "=", "human")

# Step 3: Hob robot without thermometers: heating is a logged estimate.
basic = c.dry_run(recipe_id="shakshuka", device_id="demo-hob-robot-basic", human_present=True)
assert pick(basic, "state") == "accepted", pick(basic, "state")
print("ok", "state", "=", "accepted")
assert pick(basic, "plan[3].rung") == "model", pick(basic, "plan[3].rung")
print("ok", "plan[3].rung", "=", "model")

# Step 4: Hob robot with thermometers: heating is verified by the pan thermometer.
robot = c.dry_run(recipe_id="shakshuka", device_id="demo-hob-robot", human_present=True)
assert pick(robot, "state") == "accepted", pick(robot, "state")
print("ok", "state", "=", "accepted")
assert pick(robot, "plan[3].rung") == "cw.sense.pan_surface_temp", pick(robot, "plan[3].rung")
print("ok", "plan[3].rung", "=", "cw.sense.pan_surface_temp")

# Step 5: Smart oven: it lists cw.op.heat, so the dry run gives it the pan-heating step as an estimate and leaves the rest to the person. The dry run matches operations, not equipment; the recipe's equipment list is a separate check.
oven = c.dry_run(recipe_id="shakshuka", device_id="demo-oven", human_present=True)
assert pick(oven, "state") == "accepted", pick(oven, "state")
print("ok", "state", "=", "accepted")
assert pick(oven, "plan[3].by") == "device", pick(oven, "plan[3].by")
print("ok", "plan[3].by", "=", "device")
assert pick(oven, "plan[3].rung") == "model", pick(oven, "plan[3].rung")
print("ok", "plan[3].rung", "=", "model")
assert pick(oven, "plan[4].by") == "human", pick(oven, "plan[4].by")
print("ok", "plan[4].by", "=", "human")

# Step 6: Fryer robot: same as the hob robot for this dish, thirteen steps by the device.
fryer = c.dry_run(recipe_id="shakshuka", device_id="demo-fryer-robot", human_present=True)
assert pick(fryer, "state") == "accepted", pick(fryer, "state")
print("ok", "state", "=", "accepted")
assert pick(fryer, "plan.length") == 13, pick(fryer, "plan.length")
print("ok", "plan.length", "=", 13)

print("scenario complete")

scenarios/015-browser-demo-presets.json

Previous: 014 Next: 016