Skip to content

Say a button: guide to the button you name - #66

Merged
NeelMaddu268 merged 12 commits into
mainfrom
neel/voice
Oct 15, 2026
Merged

NeelMaddu268 merged 12 commits into
mainfrom
neel/voice

Conversation

@NeelMaddu268

Copy link
Copy Markdown
Collaborator

Stacked on #65.

What changed

Say a button: say the button you want, and guidance starts to it.

  • VoiceRequest listens on the device, using the same SpeechTranscriber / SpeechAnalyzer setup as LiveCameraView (Evan's code from button ui #41). It checks what it hears against the scanned panel as the words come in, so "start" starts guidance as soon as it's heard, not after a pause. It gives up after 8 seconds. The screen shows what it heard, and says so when nothing on the panel matches.

  • ButtonLookup decides which button a request names:

    1. Asav's TargetMatcher on the whole request first.
    2. Then word by word, still through TargetMatcher so its synonyms apply ("go" is Start, "five" is 5), against each word of each label.

    That finds Start on a key read as "Start Pause" off two lines, which the matcher can't split yet. The shortest matching label wins, so "Start" beats "Start Time". Words like "press" and "please" are ignored.

  • Guide to Start uses the same lookup, replacing the temporary helper from Scan the panel from the live camera #62.

Which package

  • ContourApp — app target / wiring

Which lane

Officers (integration). This uses Experience's speech work and Surface's TargetMatcher.

How it was tested

  • xcodebuild test (ContourTests) on an iOS 27 simulator: 12 pass. 5 are new lookup tests:
    • "start", "Start", "press start please" and "go" find "Start Pause";
    • a plain label matches;
    • "five" finds 5;
    • the shortest label wins;
    • nothing on the panel finds nothing.
  • Builds for device. Harness builds. Scripts/check-dependencies.sh passes.
  • The microphone part needs a phone (the simulator can't test it). Not run yet.

Does this touch ContourCore?

  • No

A Say a button control listens on the device (the same speech setup as
LiveCameraView) and checks what it hears against the scanned panel as the
words come in, so "start" starts guidance as soon as it's heard. It gives up
after 8 seconds and shows what it heard.

ButtonLookup tries TargetMatcher on the whole request, then word by word
through TargetMatcher so its synonyms still apply, against each word of each
label. That finds Start on a key read as "Start Pause" off two lines. The
shortest matching label wins. Guide to Start uses it too.
@NeelMaddu268
NeelMaddu268 changed the base branch from neel/live-app to main October 8, 2026 23:07
…urous testing for microwaves:

Pre-Test panel:

| Photo | start | stop | pause | timer | time | clock | add 30 seconds | power |
|---|---|---|---|---|---|---|---|---|
| Angle.jpeg | nil | nil | nil | Time (0.9) | Time (1.0) | Time (0.9) | nil | nil |
| Glare.jpeg | nil | nil | nil | nil | nil | nil | nil | nil |
| MicrowaveGlare.heif | nil | nil | nil | nil | nil | nil | nil | nil |
| MicrowaveStraight.HEIC | nil | nil | nil | nil | nil | nil | nil | nil |
| Neat1.jpeg | nil | nil | nil | nil | nil | nil | nil | nil |
| Neat2.jpeg | nil | Cancel (0.9) | Cancel (0.9) | nil | nil | nil | nil | nil |
| angle-microwave.heif | nil | nil | nil | Clock (0.9) | Clock (0.9) | Clock (1.0) | nil | nil |
| angle_microwave.jpg | nil | nil | nil | nil | nil | nil | nil | nil |
| glare_microwave.jpg | Start (1.0) | Pause (0.9) | Pause (1.0) | nil | nil | nil | Add 30 Sec (0.9) | Power (1.0) |
| microwave1_angle.heif | START (1.0) | CANCEL (0.9) | CANCEL (0.9) | Clock (0.9) | Clock (0.9) | Clock (1.0) | nil | nil |
| microwave1_glare.heif | START (1.0) | CANCEL (0.9) | CANCEL (0.9) | Clock (0.9) | Clock (0.9) | Clock (1.0) | + 30 (0.9) | nil |
| microwave1_neat.heif | START (1.0) | CANCEL (0.9) | CANCEL (0.9) | Clock (0.9) | Clock (0.9) | Clock (1.0) | nil | nil |
| microwave1_neat2.heif | START (1.0) | CANCEL (0.9) | CANCEL (0.9) | Clock (0.9) | Clock (0.9) | Clock (1.0) | nil | nil |
| microwave2-glare.jpg | START (1.0) | STOP (1.0) | STOP (0.9) | Clock (0.9) | Clock (0.9) | Clock (1.0) | nil | Power (1.0) |
| microwave2-neat.jpg | nil | nil | nil | nil | nil | nil | nil | nil |
| microwave2-neat2.jpg | nil | nil | nil | nil | nil | nil | nil | nil |
| neat-microwave.heif | nil | nil | nil | nil | nil | nil | nil | nil |
| normal_microwave.jpg | nil | nil | nil | nil | nil | nil | nil | Power (1.0) |

Post-Test panel:

| Photo | start | stop | pause | timer | time | clock | add 30 seconds | power |
|---|---|---|---|---|---|---|---|---|
| Angle.jpeg | nil | nil | nil | nil | Cook Time (0.8) | nil | nil | nil |
| Glare.jpeg | nil | nil | nil | Timer on/off (0.8) | nil | nil | nil | Power Level (0.8) |
| MicrowaveGlare.heif | nil | nil | nil | nil | Time Cook (0.9) | nil | nil | nil |
| MicrowaveStraight.HEIC | nil | nil | nil | nil | nil | nil | nil | Level Power (0.8) |
| Neat1.jpeg | Start Pause (0.8) | Cancel Off (0.7) | Start Pause (0.8) | Timer on/off (0.8) | Time Cook (0.9) | Clock (1.0) | 30 Add Sec (0.8) | Power Level (0.8) |
| Neat2.jpeg | Start Pause (0.8) | Cancel (0.7) | Start Pause (0.8) | Timer on/off (0.8) | Time Cook (0.9) | Clock (1.0) | 30 Add Sec (0.8) | Power Level (0.8) |
| angle-microwave.heif | nil | nil | nil | nil | COOk Time (0.8) | Clock (1.0) | nil | Level Power (0.8) |
| angle_microwave.jpg | Start Pause (0.8) | Start Pause (0.7) | Start Pause (0.8) | Kitchen Timer (0.8) | Time Cook (0.9) | nil | nil | nil |
| glare_microwave.jpg | Start (1.0) | Cancel Off (0.7) | Pause (1.0) | Kitchen Timer (0.8) | Time Cook (0.9) | Clock (1.0) | Add 30 Sec (0.9) | Power (1.0) |
| microwave1_angle.heif | START (1.0) | CANCEL (0.7) | nil | Timer Set/Off (0.8) | Cook Time (0.8) | Clock (1.0) | 30 Sec (0.8) | Power Cook (0.8) |
| microwave1_glare.heif | START (1.0) | CANCEL (0.7) | nil | Timer SeYOT (0.8) | Cook Time (0.8) | Clock (1.0) | + 30 (0.9) | Cook Power (0.8) |
| microwave1_neat.heif | START (1.0) | CANCEL (0.7) | nil | Timer Set/Off (0.8) | Time Cook (0.9) | Clock (1.0) | + 30 Sec (0.8) | Cook Power (0.8) |
| microwave1_neat2.heif | START (1.0) | CANCEL (0.7) | nil | Timer Set/Off (0.8) | Cook Time (0.8) | Clock (1.0) | 30 Sec (0.8) | Cook Power (0.8) |
| microwave2-glare.jpg | START (1.0) | STOP (1.0) | STOP (0.7) | nil | nil | Clock (1.0) | nil | Power (1.0) |
| microwave2-neat.jpg | nil | STOP CANCEL (0.8) | STOP CANCEL (0.7) | nil | nil | nil | nil | nil |
| microwave2-neat2.jpg | START →vûsec. (0.8) | nil | nil | Timer (1.0) | Time Cook (0.9) | Clock (1.0) | nil | nil |
| neat-microwave.heif | nil | nil | nil | Timer (1.0) | nil | Clock (1.0) | - 30 sec (0.8) | Level Power (0.8) |
| normal_microwave.jpg | Start Pause (0.8) | Cancel Off (0.7) | Start Pause (0.8) | Kitchen Timer (0.8) | nil | Clock (1.0) | 30 Sec (0.8) | Power (1.0) |
AsavGupta and others added 6 commits October 14, 2026 15:28
ContentView conflicted with #69's guidance flow. Say a button now goes
through it too: guidanceTarget finds Start with ButtonLookup in place of
startButton, and both buttons start guidance through startGuidance(to:),
which cancels any running guidance and tracks it by guidanceID. Say a
button is disabled when Guide to Start is (no map, canGuide false, a scan
running), and refuses a heard button if canGuide has gone false or the
panel was replaced while it listened.

Listening is a tracked task like scanning: scan, the manual tracking test
and stopWork (leaving the screen, going to the background) cancel it, and
a cancelled request never starts guidance.
Scanning again or leaving the screen cancels the request. Before, the mic
stayed on until the 8 second limit; now cancelling ends it right away, so
a new request doesn't have to wait for the old one to let go of the mic.
#78 goes into main before this PR. Merging it here so ButtonLookup can be
built on its whole-word matching instead of working around it.
With #78's whole-word matching, TargetMatcher finds Start on a key read
as "Start Pause" by itself, so the word-by-word fallback and the
shortest-label rule aren't needed. They also went around the matcher's
guards: "one" found the 1 in the 1:35 display, "start" found Delay Start
on an oven with no Start key, "popcorn" found a 20-word label merged off
glare, and "cook time" found Cook instead of Time Cook. Each now returns
nil or the right key, with a test for each.

Guide to Start picks the same key as before on all 23 panels in #78's
real labels, except the oven, where it used to pick Delay Start and now
finds nothing.
@NeelMaddu268
NeelMaddu268 merged commit f376264 into main Oct 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants