Summary
URCDispatcher emits quoted DTMF payloads literally when a modem reports +DTMF: "5". That makes IVR code see the digit as "5" instead of 5, so DTMFCollector, terminator handling, and menu routing can miss otherwise valid keypresses on modems that quote DTMF URCs.
Affected source
callstack/protocol/urc.py:89-93 splits the URC after : and uses .strip() only:
digit = parts[1].strip() if len(parts) > 1 else ""
- then emits
DTMFEvent(digit=digit).
tests/test_urc.py covers unquoted DTMF examples but not quoted modem output.
Minimal reproduction
Command run on clean main (23c53805bb63c0aaa7676033b92d108f32de7d81):
PYTHONPATH=. uv run --no-project --with pyserial-asyncio python - <<'PY'
import asyncio
from callstack.events.bus import EventBus
from callstack.events.types import DTMFEvent
from callstack.protocol.urc import URCDispatcher
async def main():
bus = EventBus(); urc = URCDispatcher(bus)
async with bus.stream(DTMFEvent) as stream:
await urc.dispatch('+DTMF: "5"')
event = await stream.next(timeout=1.0)
print({'digit': event.digit, 'len': len(event.digit), 'equals_5': event.digit == '5'})
asyncio.run(main())
PY
Observed output:
{'digit': '"5"', 'len': 3, 'equals_5': False}
Expected behavior
Quoted and unquoted DTMF URCs should emit the same normalized single-character digit (0-9, *, #, A-D). Invalid/multicharacter values should be ignored or logged rather than delivered as public DTMF digits.
Actual behavior
The quotes are preserved in the public DTMFEvent, so downstream code receives a three-character string and comparisons against "5", # terminators, and menu options fail.
Suggested fix direction
Normalize DTMF payloads in one place in URCDispatcher (or a small parser helper): trim whitespace, strip one matching quote pair, validate against the existing ATCommand._VALID_DTMF alphabet or an equivalent public constant, and add tests for quoted +DTMF, unquoted +DTMF, and RXDTMF forms.
Acceptance criteria
+DTMF: "5" emits DTMFEvent(digit="5").
+DTMF: 5 and RXDTMF: 5 continue to emit "5".
- Invalid quoted/multicharacter payloads do not reach IVR collectors as valid digits.
- A regression test proves
CallSession.collect_dtmf() / DTMFCollector handles a quoted digit and quoted terminator.
Verification gates
git diff --check
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/ -q
Duplicate search performed
No existing issue/PR was found with:
DTMF quoted digit URC in:title,body
+DTMF quotes IVR collect digit in:title,body
- PR search
DTMF quoted digit URC
Summary
URCDispatcheremits quoted DTMF payloads literally when a modem reports+DTMF: "5". That makes IVR code see the digit as"5"instead of5, soDTMFCollector, terminator handling, and menu routing can miss otherwise valid keypresses on modems that quote DTMF URCs.Affected source
callstack/protocol/urc.py:89-93splits the URC after:and uses.strip()only:digit = parts[1].strip() if len(parts) > 1 else ""DTMFEvent(digit=digit).tests/test_urc.pycovers unquoted DTMF examples but not quoted modem output.Minimal reproduction
Command run on clean
main(23c53805bb63c0aaa7676033b92d108f32de7d81):Observed output:
Expected behavior
Quoted and unquoted DTMF URCs should emit the same normalized single-character digit (
0-9,*,#,A-D). Invalid/multicharacter values should be ignored or logged rather than delivered as public DTMF digits.Actual behavior
The quotes are preserved in the public
DTMFEvent, so downstream code receives a three-character string and comparisons against"5",#terminators, and menu options fail.Suggested fix direction
Normalize DTMF payloads in one place in
URCDispatcher(or a small parser helper): trim whitespace, strip one matching quote pair, validate against the existingATCommand._VALID_DTMFalphabet or an equivalent public constant, and add tests for quoted+DTMF, unquoted+DTMF, andRXDTMFforms.Acceptance criteria
+DTMF: "5"emitsDTMFEvent(digit="5").+DTMF: 5andRXDTMF: 5continue to emit"5".CallSession.collect_dtmf()/DTMFCollectorhandles a quoted digit and quoted terminator.Verification gates
Duplicate search performed
No existing issue/PR was found with:
DTMF quoted digit URC in:title,body+DTMF quotes IVR collect digit in:title,bodyDTMF quoted digit URC