pfod (Protocol For Operations Discovery) V3.0.22 - Comprehensive Technical Summary
=====================================================================================
Source: pfodSpecificationV3.0.22.rtf
(c) 2010-2023 Forward Computing and Control Pty. Ltd.

This summary covers everything an AI needs to write code to:
  1. Parse and interpret messages coming FROM a pfodDevice TO a pfodApp
  2. Display the menus, screens, drawings etc. that those messages define
  3. Send the correct response messages FROM the pfodApp BACK TO the pfodDevice

---

SECTION 1: MESSAGE FRAMING
==========================

All pfod protocol messages (except streaming raw data) are enclosed by { and }.

  - Opening delimiter: { (ASCII 0x7B)
  - Closing delimiter: } (ASCII 0x7D)
  - pfodApp -> pfodDevice: total message length must be <= 255 bytes including { and }
  - pfodDevice -> pfodApp: total message length must be <= 1023 bytes including { and }

If a message from pfodApp does not terminate within 255 bytes, the pfodDevice must
ignore the entire message as invalid.

If a message from pfodDevice exceeds 1023 bytes, pfodApp truncates at 1023 and treats
the 1023rd character as followed by an implicit } end character.

Streaming raw data is NOT enclosed in { } and can be sent at any time the pfodDevice
is not sending a regular message, provided the raw data does not contain the start of
any recognized message type (see Section 9).

In this summary, { and } represent the actual brace characters used as delimiters.

---

SECTION 2: RESTRICTED CHARACTERS
==================================

The following five characters are RESTRICTED and cannot appear literally in message
fields (titles, prompts, strings, display text, commands):

  {   (left brace, 0x7B)   - message start delimiter
  }   (right brace, 0x7D)  - message end delimiter
  `   (grave accent, 0x60) - integer field prefix
  ~   (tilde, 0x7E)        - string field prefix
  |   (pipe/bar, 0x7C)     - command field separator

Escape Sequences (ALL pfodApp implementations must support these):
  &#96;    for `   (grave accent)
  &#123;   for {   (left brace)
  &#124;   for |   (pipe)
  &#125;   for }   (right brace)
  &#126;   for ~   (tilde)
  &amp;    for &   (ampersand - rarely needed)
  &lt;     for <   (less-than - only needed to display formatting code literally)
  &#92;    for \   (backslash - only needed to prevent Unicode sequence conversion)

Only these escape sequences are supported. All other text that is not a valid
format code is passed through unchanged.

Unicode entry: Typing \uXXXX (e.g. \u2109 for degF symbol) in String Input screens
and TouchActionInput dialogs is automatically converted to the UTF-8 byte sequence.
To prevent conversion, use &#92;uXXXX.

Non-display character: The character \302\200 (UTF-8 octal) is a non-blank, non-display
character. Useful for forcing pfodApp to display a blank prompt (pfodApp will not display
an all-whitespace prompt).

---

SECTION 3: MESSAGES THE pfodApp SENDS TO A pfodDevice
======================================================

All messages are <= 255 bytes including { and }.
Format notation: [ ] = optional, [ ]* = zero or more

3.1 requestCurrentTime / setLanguageFormat
    Format: {@[~<language1>]*}
    First message sent on connection. Requests pfodDevice's current time and preferred
    language. pfodDevice replies with empty {} or a {@...} language/time response.
    Examples:
      {@}              - request default language and current time
      {@~fr~en}        - request French, or English if French not supported

3.2 getMainMenu
    Format: {.}
    Sent when pfodApp first connects (or after setLanguage). Requests pfodDevice's main
    (top-level) menu. pfodDevice must respond with its main menu or other pfodDevice msg.

3.3 Command
    Format: {<cmd>}
    Format: {<cmd>`<intArg1>[`<intArg2>]*}
    Format: {<cmd>~<strArg1>}
    Sends a command the pfodDevice has advertised. The pfodDevice MUST always respond.
    ~ precedes string argument, ` precedes integer argument.
    <cmd> is either . (dot) or starts with alpha/underscore then alphanumeric/underscore.
    <cmd> cannot have leading/trailing whitespace.
    Examples:
      {A}              - command A with no arguments
      {A`40}           - command A with integer arg 40
      {s~Hello World}  - command s with string arg "Hello World"
      {n`25}           - command n with integer arg 25
      {A~b`26`8`0}     - drawing touch command: menu cmd A, touchZone cmd b, col 26, row 8, type 0
      {A~di`3`0`0~45.44} - drawing touch + touchActionInput text

3.4 Menu Refresh Request
    Format: {<version>:<cmd>}
    Sent when pfodApp has a cached menu with a matching version. Indicates to pfodDevice
    that pfodApp already has this menu cached and only needs updates. pfodDevice can
    respond with {; ...} update or full {, ...} menu. pfodDevice must strip <version>:
    prefix and treat it as the bare <cmd>.
    Example: {v1:.}  - refresh request for main menu (.) with cached version v1

3.5 closeConnection
    Format: {!}
    Last message sent before closing. pfodDevice does not need to respond.

3.6 keepAlive
    Format: { }  (a single space between braces)
    Sent on WiFi connections. pfodDevice MUST respond with empty {}.

3.7 Secure Connection (see Section 12)
    Format: {_} and {_|<response>}
    Used to negotiate a secure connection.

---

SECTION 4: MESSAGES THE pfodDevice SENDS TO pfodApp
====================================================

All messages are <= 1023 bytes including { and }.
These messages are ONLY sent in response to a command from pfodApp.

The message type is identified by the first character after the opening {:

  {}          Empty message
  {@...}      Current Time / Language
  {,...}      Menu
  {;...}      Update menu items
  {=...}      Streaming raw data (display or plotting)
  {'...}      String input screen
  {#...}      Numeric input screen
  {?...}      Single Selection input
  {*...}      Multiple Selection input
  {!...}      Close connection (display prompt then close)
  {+...}      Drawing (Dwg) data
  {<}         Go Back
  {<<}        Go Home
  {_...}      Secure connection negotiation

In general within pfodDevice messages:
  | precedes a command field
  ~ precedes a string field
  ` precedes an integer field

4.1 Empty Message
    Format: {}
    Sent when no other message is appropriate. Confirms pfodDevice received and acted
    on last command. No spaces between { and }.
    When sent in response to a menu item command: the current menu screen remains
    displayed (user can continue making selections).
    When sent in response to a user input command: the input screen is dismissed.

4.2 Current Time / Language Message
    Format: {@[~<language>][~<yyyy/MM/dd HH:mm:ss[.SSS]>][`<ms_current>]}
    Sent in response to pfodApp's {@} message.
    Fields can appear in any order after the @.
    ~<language>: ISO 639-1 language code (e.g. ~fr, ~en). Omit for default (English/UTF-8).
    ~<yyyy/MM/dd HH:mm:ss.SSS>: pfodDevice's current date/time (decimals optional).
    `<ms_current>: pfodDevice's current millis() value.
    Examples:
      {}                              - no language/time support
      {@~fr}                          - use French
      {@`459906}                      - current ms is 459906
      {@~2019/04/09 08:01:33.535}     - current date/time
      {@~2019/04/09 08:01:33`459906}  - both date/time and ms

4.3 Back and Home Messages
    {<}    - pfodApp goes back to the previous menu
    {<<}   - pfodApp goes back to the main/home menu

4.4 Menu Message - see Section 5

4.5 Update Menu Message - see Section 7

4.6 Streaming Raw Data - see Section 9

4.7 String Input - see Section 10

4.8 Numeric Input - see Section 11

4.9 Single Selection - see Section 12

4.10 Multiple Selection - see Section 13

4.11 Close Connection
    Format: {!<prompt>}
    pfodApp displays <prompt> to user then closes the connection.
    Example: {!The Hall Light is Off}

4.12 Drawing (Dwg) Data - see Section 8

---

SECTION 5: MENU MESSAGE FORMAT
================================

Format:
  {,[<bgColour>][<promptFormat>]~<title>[`<re-requestTime ms>][~<version>][|<item>]*}

Fields:
  [<bgColour>]        optional background colour for entire menu (e.g. <bg bl>)
  [<promptFormat>]    optional text formatting applied only to the title/prompt
  ~<title>            the title shown at bottom of screen (can be empty: ~)
  `<re-requestTime ms>   optional, ms interval for pfodApp to re-request this menu (0=never)
  ~<version>          optional menu version string (a-z A-Z 0-9 . _). Enables caching.
  |<item>             zero or more menu items, each preceded by |

Version string rules: only a-z A-Z 0-9 . (dot) _ (underscore). pfodApp ignores versions
not following this format. Leading/trailing spaces trimmed.

Menu items with empty <cmd> (all whitespace) are ignored.
Menu items with duplicate <cmd> within the same menu: duplicates ignored.

Examples:
  {,}                       - empty menu, no title
  {,~Main Menu}             - menu with title, no items
  {,~Main Menu`1000}        - menu re-requested every 1 second
  {,~Main Menu~v1|A~Button} - menu with version v1, one button
  {,<bg 000040>~My Menu~v1|A<bg bl>~Button|!B~Label}

---

SECTION 5.1: MENU ITEM FORMAT
==============================

General format:
  |<itemType><cmd><itemFormats>~<text>`<integer>...

<itemType>:
  (omitted) = button or slider
  !         = label (non-interactive, no rounded border, never sends a command)
  ^         = navigation button
  +         = Drawing (Dwg) menu item
  !+  or +! = Drawing label (non-interactive)

<cmd>:
  Either . (dot) or a string starting with alpha or _ then alphanumeric or _.
  Cannot have leading/trailing whitespace. Usually single character.
  The . (dot) command re-displays the home/main menu screen.

<itemFormats> (can appear in any order after <cmd>):
  'Sticky' formatting (persists across menu updates, need not be re-sent in updates):
    <bg colour>   - background colour for this item
    <b>           - bold text style
    <i>           - italic text style
    <u>           - underline text style
    <+N> <-N>     - font size increase/decrease
    <colourCode>  - font colour (RGB hex, palette number, or colour name)
  'Non-sticky' flags (must be re-sent in update if still wanted):
    !   - disable this item (after cmd, not before - before cmd makes it a label)
    -   - hide this item (not visible, cannot be clicked)
    +   - make this item flash
    @   - play sound for this connection

---

SECTION 5.2: TYPES OF MENU ITEMS
==================================

5.2.1 Plain Label
  Format: |!<cmd><itemFormats>~[<displayString>]
  Non-interactive. Never sends a command. The ! is BEFORE the <cmd>.
  Example: |!A<bg bl>~Label text

5.2.2 Empty Label (Spacer)
  Format: |!<cmd><itemFormats>
  Use <-N> font size to control spacer height.
  Example: |!s1<-18>   (invisible spacer)

5.2.3 Plain Text Button
  Format: |<cmd><itemFormats>~[<displayString>]
  Clicking sends {<cmd>} to pfodDevice.
  Example: |A<bg bl>~Button text

5.2.4 Disabled Button
  Add ! AFTER cmd: |<cmd>!<itemFormats>~<text>
  Example: |A!<bg bl>~Button   (disabled, clicking does nothing)
  To re-enable, send update: {;|A}  (clears all non-sticky flags)

5.2.5 Hidden Button
  Add - AFTER cmd: |<cmd>-<itemFormats>~<text>
  Example: |A-<bg bl>~Button  (hidden)
  To show: send update {;|A}

5.2.6 Toggle Button (2 options)
  Format: |<cmd><itemFormats>`<idx>~[<leadingText>]~[<trailingText>]~<opt0><opt1>[~<format>]
  <idx>: 0 or 1, which option is currently displayed
  <opt0><opt1>: the two text options separated by the | character (escaped as &#124; in code)
  <format>: t = text only (no slider), s = slider only (no text)
  Clicking toggles and sends {<cmd>`<newIdx>}
  Example: |A<bg bl>`1~Button is in the ~ state~Off&#124;On
  Creates: "Button is in the On state" with a slider

5.2.7 Toggle Label (2 options, non-interactive)
  Format: |!<cmd><itemFormats>`<idx>~[<leadingText>]~[<trailingText>]~<opt0><opt1>[~<format>]
  Never sends a command. Can be updated with {;|<cmd>`<newIdx>}

5.2.8 Text Slider Button (multiple options)
  Format: |<cmd><itemFormats>`<idx>~[<leadingText>]~[<trailingText>]~<opt0><opt1>...<optN>[~<format>]
  <idx>: 0 to (N), which option is currently displayed
  Moving slider sends {<cmd>`<newIdx>}

5.2.9 Text Slider Label (non-interactive multi-option)
  Same as Text Slider Button but with ! before <cmd>.
  Can be updated with {;|<cmd>`<newIdx>}

5.2.10 Numeric Slider Button
  Format: |<cmd><itemFormats>`<currentValue>~[<leadingText>]~[<trailingText>]`<maxValue>`<minValue>~<maxScaleValue>~<minScaleValue>
  <currentValue>, <maxValue>, <minValue>: integers (data range)
  <maxScaleValue>, <minScaleValue>: floating point strings (display range)
  Display formula: displayValue = minScaleValue + (currentValue - minValue) * (maxScaleValue - minScaleValue) / (maxValue - minValue)
  Moving slider sends {<cmd>`<newValue>} where newValue is in range [minValue, maxValue]
  To show + sign in display, at least one scaleValue must have a + sign (e.g. +5.0)
  Example: |A<bg bl>`40~Slider ~%`100`0~5.0~-5.0

5.2.11 Numeric Slider Label (non-interactive)
  Same as Numeric Slider Button but with ! before <cmd>.

5.2.12 Navigation Buttons
  Format: |^<cmd><itemFormats>~<text>  (up to 5 consecutive, displayed as nav row)
  Groups of up to 5 consecutive ^-prefixed items form a navigation row.
  Displays as: Left, Right, Up, Down, Home buttons in order.
  Grouping ends when any non-navigation item is found.
  Hidden with - flag: |^<cmd>-   (hidden nav button)
  Clicking sends {<cmd>} to pfodDevice.
  Example: |^A~Left|^B~Right|^C~Up|^D~Down|^E~Home

5.2.13 Drawing (Dwg) Menu Item
  Format: |+<cmd><itemFormats>~<loadCmd>
  + before <cmd> indicates a Drawing row.
  <loadCmd> is the command pfodApp sends when requesting Drawing data.
  pfodApp sends {<loadCmd>`0} to load first part of drawing.
  If Drawing already cached, pfodApp sends {<version>:<loadCmd>`0} to refresh.
  Example: |+A~a   (menu item cmd=A, drawing load cmd=a)
  Dwg label (non-interactive): |!+<cmd>~<loadCmd> or |+!<cmd>~<loadCmd>

---

SECTION 6: TEXT FORMATTING
============================

Text formatting can be applied to <title>, <displayString>, and drawing text labels.
Formatting is optional - do not rely on it to convey meaning.
New line sequences: CR (0x0D), CR+LF (0x0D 0x0A), or LF (0x0A) all represent newline.
Tags are automatically terminated at end of title/displayString.
Enclosed tags auto-terminate when outer tag terminates.
Unrecognized tags are displayed literally as part of the text.

6.1 Style Formats
  <u>...</u>   Underline
  <b>...</b>   Bold
  <i>...</i>   Italic
  Tags can be nested. Duplicate tags cause no error.
  Closing tag terminates enclosed tags automatically.

6.2 Font Size Formats
  <+1>...</+1>   increase font size (+ sign mandatory for positive)
  <+2>...</+2>   larger increase
  <-1>...</-1>   decrease font size
  <-2>...</-2>   larger decrease
  <+0> or <-0>   zero change (must have + or -)
  Size changes are logarithmic and reciprocal.
  Reference: pfodApp uses <+6> to double size, <-6> to halve.

6.3 Font Colour Format
  <RRGGBB>...</RRGGBB>   hex RGB (e.g. <FF0000> for red)
  <N>...</N>             256-colour palette number (e.g. <9> for red)
                         NOTE: numbers without + or - are colour palette, not font size
  Named colours (16 standard):
    <bk>  black    (0)   <000000>
    <m>   maroon   (1)   <800000>
    <g>   green    (2)   <008000>
    <o>   olive    (3)   <808000>
    <n>   navy     (4)   <000080>
    <p>   purple   (5)   <800080>
    <t>   teal     (6)   <008080>
    <s>   silver   (7)   <C0C0C0>
    <gy>  grey     (8)   <808080>
    <r>   red      (9)   <FF0000>
    <l>   lime     (10)  <00FF00>
    <y>   yellow   (11)  <FFFF00>
    <bl>  blue     (12)  <0000FF>
    <f>   fuchsia  (13)  <FF00FF>
    <a>   aqua     (14)  <00FFFF>
    <w>   white    (15)  <FFFFFF>
  Special: <bw> - auto-chooses black or white for best contrast (default when no colour set)

6.4 Background Colour Format
  <bg <colourCode>>   e.g. <bg r>, <bg FF0000>, <bg bl>, <bg 12>
  Applied to the WHOLE menu item or menu/update command.
  Must follow {, or {; command or the item's <cmd>, NOT inside <displayString>.
  No closing tag. </bg r> is invalid.
  Example: {,~Menu|A<bg bl>~Button}   (blue background on button A)
  For whole menu background: {,<bg bl>~Title|...}

6.5 Sound
  <ex s>   play sound (pfodApp plays configured notification sound)

6.6 Default Colours
  If no colour set: black menu background, <bw> (contrasting black or white) for text.

---

SECTION 7: UPDATE MENU MESSAGE FORMAT
=======================================

Format:
  {;[<bgColour>][~<title if changed>][`<re-requestInterval if changed>][|<item>]*}

  - Updates the currently displayed menu screen
  - Menu items matched by <cmd>
  - Cannot add new items or change item types (button vs label vs nav vs dwg)
  - Cannot change the <version> of a menu
  - Only needs to send <cmd> for each changed item (! and ^ not needed in update)
  - If <re-requestTime ms> is missing, current interval unchanged; set to 0 to remove

Examples:
  {;}                       - does nothing (empty update)
  {;|A`5}                   - updates slider/toggle item A to value 5
  {;|A}                     - clears all non-sticky flags (re-enables hidden/disabled btn A)
  {;|A!}                    - disables button A
  {;|A-}                    - hides button A
  {;~New Title}             - updates menu title
  {;`5000}                  - sets re-request interval to 5000ms
  {;`0}                     - removes re-request interval

Slider update rule: pfodDevice MUST respond to every slider command (even intermediate
ones while user is dragging). Respond with {;|<cmd>`<currentValue>}.
While user is adjusting slider, pfodApp may update only the screen prompt from {;~<prompt>...}.
When user stops adjusting, pfodApp applies full update response.

---

SECTION 8: DRAWING (DWG) SYSTEM
==================================

8.1 Overview
  Drawings are custom graphical controls displayed within a Drawing menu item.
  Drawing menu item in menu: |+<menuCmd>~<loadCmd>
  pfodApp sends {<loadCmd>`0} to load drawing (first chunk).
  If more data: pfodApp sends {<loadCmd>`1}, {<loadCmd>`2}, etc.
  If cached version: pfodApp sends {<version>:<loadCmd>`0}.
  Drawing size: max 255x255 cols x rows. (0,0) is top-left.

8.2 Drawing Message Format (initial load / new drawing)
  Format:
    {+[<bgColour>]`<width>`<height>[~<moreData>[`<refreshInterval>][~<version>]]<dwgItems>}

  Fields:
    `<width>`<height>   required, defines drawing canvas size (cols x rows)
    [<bgColour>]        optional background colour (defaults to black if missing)
    ~<moreData>         if exactly 'm', pfodApp requests next part with offset+1
                        Any other value (or missing) = no more data
    `<refreshInterval>  optional, ms for automatic refresh (requires <version>)
    ~<version>          optional, drawing version string (same format as menu version)
                        If ~ for moreData is present, ~ for version is also required
                        even if version is empty
    <dwgItems>          drawing primitive items (see 8.4)

  When moreData = 'm':
    pfodApp sends {<loadCmd>`-1}, {<loadCmd>`-2} etc. (negative offset for continuation)
    Continuation uses the Update format (see 8.3)
    When no more data: omit moreData field, or reply {} empty, or send {+~m} (no items)

  Example: {+15`50`50~m`0~V1|z~0~0|r~12~40~40~5~5|r~9~30~30~10~10|z}
    Background colour 15 (white), 50x50 canvas, more data follows, no refresh, version V1

8.3 Drawing Update Message (refresh / continuation)
  Format: {+[~<moreData>]|<dwgItems>}
  Used when parser.isRefresh() is true, or for continuation parts.
  NOTE: In current pfodApp, updates/refreshes must fit in single message (moreData ignored for updates).
  Only updates existing primitives (by z-index) or touchZones (by cmd).
  Example: {+|t`5~9~25~25~15}  (updates text item at index 5)

8.4 Drawing Coordinate System
  - (0,0) is top-left corner of drawing
  - Positive col = right, positive row = down
  - Drawing is scaled to fill available screen width (aspect ratio preserved)
  - If height > available space, scaled down to fit
  - Drawing primitives can use fractional positions (float col/row)
  - pushZero() establishes new origin and scale factor on a stack
  - popZero() restores previous origin and scale factor

8.5 Drawing Primitives Wire Format Summary

  Line:
    |l[`idx]~[<colour>]~colDelta~rowDelta[~colOffset[~rowOffset]]
    dwgs.line().idx(N).color(C).size(dC,dR).offset(col,row).send()

  Rectangle (unfilled):
    |r[c][`idx]~[<colour>]~width~height[~colOffset[~rowOffset]]
    c = centered (default: top-left origin)
    dwgs.rectangle().centered().idx(N).color(C).size(w,h).offset(col,row).send()

  Filled Rectangle:
    |R[c][`idx]~[<colour>]~width~height[~colOffset[~rowOffset]]
    dwgs.rectangle().filled().centered()...send()

  Rounded Rectangle:
    |rr[c][`idx]~[<colour>]~width~height[~colOffset[~rowOffset]]
    dwgs.rectangle().rounded().centered()...send()

  Filled Rounded Rectangle:
    |RR[c][`idx]~[<colour>]~width~height[~colOffset[~rowOffset]]
    dwgs.rectangle().filled().rounded().centered()...send()

  Circle (unfilled):
    |c[`idx]~[<colour>]~dRadius[~colOffset[~rowOffset]]
    dwgs.circle().idx(N).color(C).radius(r).offset(col,row).send()

  Filled Circle:
    |C[`idx]~[<colour>]~dRadius[~colOffset[~rowOffset]]
    dwgs.circle().filled()...send()

  Arc (unfilled):
    |a[`idx]~[<colour>]~dArcAngle~dStartAngle~dRadius[~colOffset[~rowOffset]]
    dwgs.arc().idx(N).color(C).radius(r).start(deg).angle(deg).offset(col,row).send()
    Default: dStartAngle=0, dArcAngle=360 (full circle)

  Filled Arc:
    |A[`idx]~[<colour>]~dArcAngle~dStartAngle~dRadius[~colOffset[~rowOffset]]
    dwgs.arc().filled()...send()

  Text Label:
    |t[`idx]~[<colour>]~text[~colOffset[~rowOffset[~alignment]]]
    alignment: C (center, default), L (left), R (right)
    Text can include all formatting tags except <bg..>.
    Restricted chars in text() are automatically escaped by pfodParser library.
    dwgs.label().idx(N).color(C).text("...").fontSize(N).bold().italic().underline()
        .offset(col,row).center().send()
    For float values: add .units("...").decimals(N).floatReading(val)
      (formatting and scaling done in pfodParser before send)

  Value (mapped integer) Text:
    |v[`idx]~[<colour>]~text~colOffset~rowOffset`value~units`min`max~displaymin~displaymax`decimals[~alignment]
    The integer value is mapped: displayValue = displaymin + (value-min)*(displaymax-displaymin)/(max-min)
    Then formatted to decimals places and displayed as: text + displayValue + units
    Scaling done in pfodApp at display time (value stored as integer).
    dwgs.label().idx(N).color(C).text("...").offset(col,row).intValue(val)
        .minValue(0).maxValue(1).displayMin(0.0).displayMax(1.0).decimals(2).send()

  Hide:
    |h~<cmd>        - hides touchZone with this cmd
    |h`<idx>        - hides primitive with this index
    dwgs.hide().cmd(cmd).send()  or  dwgs.hide().idx(idx).send()
    Does NOT work for insertedDwgs (use EraseDwg/HideDwg instead)

  Unhide:
    |uh~<cmd>       - unhides touchZone with this cmd
    |uh`<idx>       - unhides primitive with this index
    dwgs.unhide().cmd(cmd).send()  or  dwgs.unhide().idx(idx).send()

  Erase:
    |e~<cmd>        - completely removes touchZone with this cmd AND any insertedDwg with this loadCmd
    |e`<idx>        - completely removes primitive with this index
    dwgs.erase().cmd(cmd).send()  or  dwgs.erase().idx(idx).send()

  Zero Push (pushZero):
    |z~col~row~scaling
    Pushes current (zero, scaling) onto stack. Sets new zero = current_zero + (col*current_scale, row*current_scale).
    New scaling = current_scale * new_scaling. scaling defaults to 1.0 if missing.
    dwgs.pushZero(col, row, scaling)   (no send() needed)
    Initially: zero=(0,0), scaling=1.0

  Zero Pop (popZero):
    |z
    Pops previous (zero, scaling) from stack. If stack empty, uses (0,0), 1.0.
    dwgs.popZero()   (no send() needed)

  Index:
    |i`<idx>
    Reserves this index with current zero/scaling for later update. Nothing displayed.
    dwgs.index().idx(N).send()
    WARNING: In current pfodApp, sending index() with same index as existing primitive
    replaces that primitive (effectively hiding it). Use hide()/erase() instead.

  HideDwg:
    |hd~<loadcmd>
    Hides an inserted drawing (by loadCmd). Does not delete it.
    dwgs.hide().loadCmd(cmd).send()

  UnhideDwg:
    |uhd~<loadcmd>
    Restores hidden inserted drawing.
    dwgs.unhide().loadCmd(cmd).send()

  EraseDwg:
    |ed~<loadcmd>
    Completely removes inserted drawing and all its contents recursively.
    dwgs.erase().loadCmd(cmd).send()

  InsertDwg:
    |d~<loadcmd>~~colOffset~rowOffset
    Loads drawing via loadcmd, merges at current position with current scaling.
    colOffset/rowOffset: point in inserted dwg that maps to current zero.
    E.g. offset(5,3) means position (5,3) in inserted dwg maps to current (0,0).
    All indices and touchZone cmds must be unique across both drawings.
    A dwg cannot be inserted more than once (subsequent insertions ignored).
    dwgs.insertDwg().loadCmd('z').offset(5,3).send()

8.6 Z-Order and Indexing
  - Non-indexed items: drawn first, in order received
  - Indexed items (`idx > 0): drawn after non-indexed, lowest index first
  - idx integer > 0 (0 means no index assigned)
  - Index allows later update/erase by reference
  - pfodAutoIdx library class provides up to 999 globally unique indices

8.7 TouchZone
  Format: |x[c][`priority]~<cmd>~width~height[~colOffset[~rowOffset]][`touchFilter]
  Defines a rectangular interactive area.
  c = centered (default: top-left origin)
  cmd: unique identifier for this touchZone
  priority: optional, used for overlap resolution (higher = on top)
  pfodApp expands all touch regions by 4.5mm on each side (min ~9mm x 9mm).
  TouchZones uniquely referenced by their cmd.

  Touch Filter values (integer, can OR combine except TOUCH and DISABLED):
    0   TOUCH         - send cmd once, block until response (default if omitted)
    1   DOWN          - send cmd on finger down
    2   DRAG          - send cmd on finger drag
    4   UP            - send cmd on finger lift
    8   CLICK         - send cmd on click
    16  LONG_PRESS    - send cmd on long press
    256 DOWN_UP       - detect down+drag, but only send cmd on finger up
    512 TOUCH_DISABLED - ignore all touches (overrides all other filters)

  Overlap resolution (when touch lands in multiple zones):
    1) Without priorities: outer (enclosing) zone wins; or "best fit" (most centered) wins
    2) With priorities: highest priority wins (missing priority = 0)

  Command returned when zone touched:
    {<menuCmd>~<touchZoneCmd>`<col>`<row>`<touchType>[~<editedText>]}
    where:
      <menuCmd>      = menu item cmd (the + Dwg row's cmd)
      <touchZoneCmd> = cmd of the triggered touchZone
      <col>          = unscaled column touched (integer)
      <row>          = unscaled row touched (integer)
      <touchType>    = 0=TOUCH, 1=DOWN, 2=DRAG, 4=UP, 8=CLICK, 16=LONG_PRESS
      <editedText>   = text from touchActionInput dialog (if any)

  Parsing in pfodDevice:
    byte dwgCmd = parser.parseDwgCmd()   returns first char of active (touchZone) cmd
    parser.getTouchedCol()   returns col touched
    parser.getTouchedRow()   returns row touched
    parser.getTouchedType()  returns touch type integer
    parser.isPress(), isClick(), isDown(), isDrag(), isUp()  check touch type
    parser.getEditedText()   returns char* to edited text (un-escaped)
    parser.dwgCmdEquals(d_cmd)  compares dwgCmd to string

8.8 TouchAction
  Format: |X~<cmd>~<DrawingPrimitive>
  Triggered immediately when associated touchZone (same cmd) is triggered.
  Updates the drawing immediately on screen WITHOUT waiting for pfodDevice response.
  One touchZone can have multiple touchActions.
  The DrawingPrimitive can use TOUCHED_COL and TOUCHED_ROW special values:
    dwgs.TOUCHED_COL  replaced with touched column at trigger time
    dwgs.TOUCHED_ROW  replaced with touched row at trigger time
  When actual pfodDevice response arrives, touchAction modifications are discarded and
  drawing is updated by the returned message.
  If touchZone erased, all its touchActions are also removed.
  If touchZone hidden, all its touchActions are disabled.

  Example (change button colour on press):
    dwgs.circle().idx(z_idx).color(BLACK).radius(6.75).send()
    dwgs.touchZone().cmd(cmd).size(6.75,6.75).send()
    dwgs.touchAction().cmd(cmd).action(
        dwgs.circle().idx(z_idx).filled().color(BLUE).radius(8)
    ).send()

8.9 TouchActionInput
  Format: |XI~<cmd>~<promptText>[`<idxOfTextItem>]
  When touchZone with this cmd is triggered, opens a single-line text edit dialog.
  promptText: shown as dialog prompt
  idxOfTextItem: index of a label/text primitive; its current text pre-populates dialog
                 If 0, not specified, or not a text primitive: dialog starts empty
  If touchZone is not TOUCH_DISABLED, filter is forced to TOUCH.
  If user presses X: dialog abandoned, no message sent.
  If user presses tick: sends full touch command with edited text:
    {<menuCmd>~<touchZoneCmd>`<col>`<row>`<touchType>~<editedText>}
  editedText is automatically escaped for restricted chars before sending.
  parser.getEditedText() automatically un-escapes.
  Unicode sequences (\uXXXX) in dialog are auto-converted to UTF-8.
  doNotEncode(): suppress automatic escaping.
  editedText is truncated if necessary to keep message < 255 bytes.

8.10 Drawing Size Guidelines
  - Recommended width: 50 cols (touch resolution is 1/50 of displayed width)
  - Aspect ratio > 1:1.3 may cause drawing to be scaled down to fit screen
  - All primitives can be placed at fractional col/row positions
  - pushZero scaling can be used to zoom into portions of the drawing
  - Design at ~actual display size to verify text readability

8.11 Drawing Colours
  Same colour format as text formatting:
    - Integer 0-255 (256-colour palette)
    - RGB hex string FF0000
    - Named colour string (r, g, bl, etc.)
  In pfodParser library, all colours are integers from 256-colour palette.
  Default if omitted: BLACK_WHITE (auto-contrast black or white)

8.12 Reuse of Drawings
  Drawings can be reused in multiple menu items by using the same <loadCmd>.
  If a version string is specified, drawing cache persists across disconnects.

8.13 Drawing Refresh (Automatic Reload)
  Set in drawing start message: `<refreshInterval> (ms, non-zero)
  Requires a non-empty menu version.
  Only versioned drawings are refreshed.
  Refresh updates limited to single 1023-byte message.
  If insertDwg is present, inserted drawing is also reloaded.

8.14 Forcing Drawing Reload
  Send {;|<menuItemCmd>} in an update message.
  If drawing is cached: pfodApp sends {<version>:<loadCmd>`0}
  If updated to new loadCmd: pfodApp loads new drawing.
  To disable: {;|<menuItemCmd>!}
  To hide: {;|<menuItemCmd>-}

---

SECTION 9: STREAMING RAW DATA
===============================

9.1 Display Mode
  Format: {=[<title>]}
  Opens streaming raw data screen with optional title.
  pfodDevice can send any raw data at any time that does NOT contain (unescaped):
    {,  {;  {.  {:  {'  {?  {#  {*  {^  {=  {@  {}  {_
  Raw data is US-ASCII (space to ~) plus CR and LF only.
  UTF-8 bytes are saved to file but not decoded for display.
  pfodApp auto-starts saving data to file.

  Examples:
    {=}                     - open data screen, no title
    {=Temperature Readings} - open with title

9.2 Plotting Mode
  Format:
    {=[<screenTitle>][`<maxPoints>][~<plotOption>[~<plotOption>]]|[<title>][`<plotNo>[~<max>[~<min>[~<unit>]]]]
     [|[<title>][`<plotNo>[~<max>[~<min>[~<unit>]]]]]*}

  <screenTitle>: optional title for plot screen
  `<maxPoints>: optional max points to keep (default 500); oldest dropped when exceeded
  ~<plotOption>: optional, multiple allowed:
    ~C          clear all previous data, show only new data
    ~S          sort X-axis data before display (default: display in arrival order)
    Any single-letter option is valid (others ignored)
    First non-single-letter option is date/time or elapsed time format for X-axis

  X-axis format options:
    ss              seconds elapsed (e.g. 128)
    mm:ss           minutes:seconds (mins can exceed 60)
    HH:mm:ss        hours:minutes:seconds (hrs can exceed 24)
    d HH:mm:ss      days hours:minutes:seconds
    ss.S / ss.SS / ss.SSS  with fractional seconds
    Any Java SimpleDateFormat string (e.g. yyyy/MM/dd) for date/time display
    Append " UTC" for UTC timezone display
    Append " DATE" or " UTC" after elapsed format to use as date/time

  | sections:
    Number of | sections MUST equal number of CSV fields in raw data lines
    Empty | section: field ignored (not plotted)
    Non-empty | section: field plotted

  Per-field options (after |):
    [<title>]     label for this plot line / X-axis label
    `<plotNo>     integer (0 = X-axis, same abs value = same sub-plot)
                  if ALL plotNo missing: first field = X-axis
                  if ANY non-blank plotNo: fields with missing plotNo each get own sub-plot
                  if only one field to plot: plotNo ignored, X-axis = record count
    ~<max>        initial max Y value (auto-scales if data exceeds)
    ~<min>        initial min Y value (auto-scales if data below)
    ~<unit>       Y-axis label (first non-blank unit used for sub-plot)

  Date/Time raw data formats:
    yyyy/MM/dd HH:mm:ss.SSS  (trailing parts optional, e.g. 2019/04)
    Milliseconds (whole number integer)
    If ms timestamp < 2^38 (~1979): treated as offset, aligned with @response ms
    If ms timestamp > 2^38: treated as absolute ms since Jan 1 1970 UTC

  Requirements for date/time plotting:
    - pfodDevice MUST respond to {@} with current time
    - For ms timestamps: include `<ms_current> in @ response
    - For date/time timestamps: include ~<yyyy/MM/dd HH:mm:ss.SSS> in @ response
    - For both: include both in @ response

  Examples:
    {=Example Plot||time(sec)||Motor rpm|Pwm}    - 5-field data, plot rpm & pwm vs time
    {=Example Plot|rpm}                          - 1-field, plot vs record count
    {=Example Plot`20|rpm}                       - show last 20 points only
    {=Example Plot|rpm~~0}                       - min Y = 0
    {=Example Plot||time(sec)`0|RPM`1|rpm`1|}    - two lines on same sub-plot
    {=Temp/RH~E MMM/dd HH:mm|Date|Temp C~32~17|RH%~82~40}  - date/time X-axis

  Raw data is always US-ASCII (space through ~, plus CR/LF). Numbers in US-ASCII only.

---

SECTION 10: STRING INPUT SCREEN
=================================

Format: {'<cmd>[`<maxLen>][~<prompt>][|<string>]}

  <cmd>:    command to associate with response
  `<maxLen>: optional, max bytes for user input (byte limit, not char limit)
             pfodApp enforces this; default = 255 - len({<cmd>| })
  ~<prompt>: optional, prompt text displayed on input screen
  |<string>: optional, initial text to pre-populate the input field

When user submits: pfodApp sends {<cmd>~<newString>} to pfodDevice.
Input screen is dismissed after submission.

Notes:
  - pfodApp should not allow restricted characters to be entered directly
  - User must use escape sequences manually for restricted chars
  - <maxLen> is a byte limit (UTF-8 multi-byte chars count as multiple bytes)
  - pfodApp must not send partial UTF-8 sequences
  - Unicode sequences (\uXXXX) typed in input are auto-converted to UTF-8

Examples:
  {'s}                          - no prompt, no initial text, cmd=s
  {'s~Enter some test text.}    - with prompt
  {'x`11~Edit the message|Hello World}  - pre-populated, max 11 bytes

---

SECTION 11: NUMERIC INPUT SCREEN
==================================

Format: {#<cmd>[~<prompt>]`<numberCurrent>[`<numberMax>[`<numberMin>]][~units[~scale[~offset]]]}

  <cmd>:            command to associate with response
  ~<prompt>:        optional prompt text
  `<numberCurrent>: current value (0 if missing)
  `<numberMax>:     optional maximum (default 254)
  `<numberMin>:     optional minimum (default 0)
  ~units:           optional units text
  ~scale:           optional scale factor (float)
  ~offset:          optional offset (float)
  Negative numbers are supported.
  pfodApp swaps max/min if necessary so max > min, and clamps current to [min, max].

Display value shown to user: (integer_value * scale) + offset (with units appended)
Command sent when user submits: {<cmd>`<integerValue>}
Integer value is in range [min, max] (unscaled).
Input screen dismissed after submission.

Examples:
  {#n`5}                                      - no prompt, current=5, max=254, min=0
  {#n~Prompt`25`30`5}                         - with prompt, range 5-30, current=25
  {#n~Set Volts`908`1000`5~ Volts~0.011074~-0.055}  - with scale/offset/units

---

SECTION 12: SINGLE SELECTION LIST
===================================

Format: {?<cmd>[`<initialSetting>][~<prompt>]|<label0>[|<labelN>]*}

  <cmd>:              command to associate with response
  `<initialSetting>:  optional, 0-based index of initially selected item
                      if < 0 or >= count: nothing selected (reliable way: use `- 1)
  ~<prompt>:          optional prompt text
  |<label>:           one or more options (each preceded by |)

When user selects option N (0-based) and submits: {<cmd>`N} sent to pfodDevice.
Input screen dismissed after submission.

Example:
  {?i`2~Select Light Level|Off|Low|Medium|High}
  - 4 options, initially "Medium" (index 2) selected
  - If user presses accept with Medium: {i`2} sent

---

SECTION 13: MULTIPLE SELECTION LIST
=====================================

Format: {*<cmd>[`<initialSetting>]*[~<prompt>]|<label0>[|<labelN>]*}

  <cmd>:              command to associate with response
  [`<initialSetting>]*: optional, one or more `N for each initially selected index
  ~<prompt>:          optional prompt text
  |<label>:           one or more options

When user submits selection:
  - No options selected: {<cmd>|} sent
  - One option (index N): {<cmd>|N} sent
  - Multiple options (indices N1, N2, ...): {<cmd>|N1`N2...} sent
    First selected uses | prefix, subsequent use ` prefix
Input screen dismissed after submission.

Example:
  {*m`0`1~Choose options|Fade on/off|3 Levels}
  - Both options initially selected (indices 0 and 1)
  - If user sends both selected: {m`0`1}

---

SECTION 14: REFRESH AND MENU CACHING
=======================================

14.1 Refresh Without Caching
  If menu has `<re-requestTime ms> but no ~<version>:
    pfodApp re-sends the original command at that interval.
    pfodDevice must resend the full menu each time.

14.2 Menu Caching (with version)
  If menu has ~<version>:
    pfodApp caches the menu.
    On re-request, pfodApp sends {<version>:<cmd>} (refresh request).
    pfodDevice can check if version matches its current version:
      - If match: send {; ...} update only (parser.isRefresh() returns true)
      - If no match or don't care: send full {, ...} menu
    Cache persists across disconnect/reconnect for same pfodDevice.
    Version format: a-z A-Z 0-9 . _ only (pfodApp ignores invalid formats)

14.3 Drawing Caching
  Same mechanism as menu caching for drawings.
  Drawing version must be the same as the menu version (both use same pfodParser version).
  pfodApp sends {<loadCmd>`0} for initial load (no version prefix).
  pfodApp sends {<version>:<loadCmd>`0} when it has a cached drawing.
  parser.isRefresh() returns true if version matches.

---

SECTION 15: SECURE CONNECTION SEQUENCE
=========================================

Secure connections use SipHash 128-bit challenge-response. Only for WiFi connections
in reference pfodApp. Connection is terminated if both sides don't agree.

Threats protected:
  - Remote hacker connecting to open pfodDevice IP:port
  - Hacker taking over established connection
Threats NOT protected:
  - Physical access to pfodDevice or mobile
  - Denial of Service attacks

Sequence:
  1. pfodDevice listens on IP:port, remains silent on connection.
  2. If no {_} challenge request within short time, pfodDevice closes connection.
  3. pfodApp sends {_} (connection prompt).
  4. pfodDevice responds with 64-bit challenge (16 hex chars) + 2-char hash ID:
       {_FFFFFFFE00000CBC02}
     (The 02 suffix specifies SipHash as hash algorithm)
     pfodDevice also computes expected response using SipHash and 128-bit secret key.
  5. pfodApp computes response using SipHash(secret_key, challenge), sends 8 bytes hex:
       {_|BA7816BF8F01CFEA}
     Secret keys < 16 bytes are padded with trailing zeros. Full 16-byte keys recommended.
  6. pfodDevice compares response:
       - Not equal: force close connection
       - Equal: send {_} then await normal pfod messages (plain text)
  7. After connection, each plain text message is followed by 8 hex digits (4 bytes of SipHash):
       Hash input: secret_key + challenge + msgCounter + message
       Each side appends hash to its messages, checks hash of received messages.
       If hash wrong: close connection.
  8. Message counter: starts at 0, separate counts for incoming and outgoing.
     Message: from { to } inclusive.
  9. At end of connection (after challenge was sent): pfodDevice generates new challenge.

---

SECTION 16: NAVIGATION GUIDELINES
====================================

pfodDevice does NOT need to track navigation history. pfodApp handles navigation.

16.1 Responses to Menu Item Commands (pfodApp sends {<cmd>})
  pfodDevice can respond with any message except {@} language message.

  Response is {, ...} menu:
    - Becomes current menu. Previous menu goes onto pfodApp's navigation stack.
    - Back button returns to previous menu.
    - If menu system loops back on itself, pfodApp collapses the loop.

  Response is {' ...}, {# ...}, {? ...}, {* ...} (user input):
    - Input screen shown. Back button returns to last menu.

  Response is {= ...} (streaming raw data):
    - Data screen shown. Back button returns to last menu.

  Response is {} or {; ...}:
    - Current menu remains displayed.
    - {;} updates the current menu items.
    - {} leaves menu unchanged.

16.2 Responses to User Input Commands
  Simplest: return {} empty. pfodApp re-requests the previous menu.
  Other responses ({, ...} or another input) are valid but not recommended.

16.3 Re-requesting Menus
  pfodApp re-requests a menu by resending the original command that produced it.
  May add {<version>:} prefix if menu was cached.
  pfodDevice should respond with full menu (or update if version matches).
  pfodDevice MUST NOT respond with {} to a menu re-request (only exception: see spec).
  TIP: pfodDevice can always ignore refresh and send full menu.
  TIP: Add |.~Return to Main menu item to provide shortcut to main menu.

16.4 The . (dot) Command
  The . (dot) command re-requests the pfodDevice's main menu from any menu.
  When pfodApp sends {.} pfodDevice responds with main menu.

16.5 Required pfodDevice Behaviors
  - Respond to ALL messages (even unrecognized) with a reply (at minimum {})
  - Respond to {.} with main menu
  - Strip any <version>: prefix from commands before processing
  - Ignore messages > 255 bytes
  - MUST NOT send pfod messages except streaming raw data outside of responding to pfodApp

16.6 Required pfodApp Behaviors
  - Automatically send {.} on connection (or {@} first if language detection needed)
  - Display all message types from pfodDevice
  - Provide debug mode to view transmissions
  - Configurable response timeout per connection
  - Truncate received messages at 1023 chars (treat as implicit })
  - Limit all sent messages to <= 255 bytes
  - Honor pfodDevice's max/min limits for numeric inputs
  - Honor pfodDevice's max byte count for string inputs
  - Honor re-request intervals
  - Convert UTF-8 bytes to Unicode for display, Unicode input to UTF-8 before sending

---

SECTION 17: IMPORTANT RULES AND CONSTRAINTS SUMMARY
======================================================

Message Structure:
  - { starts a message, } ends it
  - All pfod messages enclosed in { }
  - Streaming raw data NOT enclosed in { }
  - pfodDevice MUST respond to EVERY message from pfodApp
  - pfodDevice must NOT spontaneously send pfod messages (except streaming raw data)
  - pfodApp sends ONE message at a time, waits for response before sending next

Command Rules:
  - <cmd> = . (dot) OR alphanumeric string starting with a-z A-Z or _
  - <cmd> cannot have leading or trailing whitespace
  - Commands must be unique within a single menu
  - <cmd> is case-sensitive
  - pfodAutoCmd can generate up to 999 unique cmds (c1 to c999)

Version Strings:
  - Only a-z A-Z 0-9 . (dot) _ (underscore)
  - Invalid versions are silently ignored by pfodApp
  - Leading/trailing spaces are trimmed

Drawing Rules:
  - Drawing size max 255x255
  - All touchZone cmds and z-indices must be unique within a drawing (including inserts)
  - Last definition wins if duplicate indices/cmds
  - A drawing cannot be inserted more than once
  - Dwg update messages must fit in single 1023-byte message
  - There MUST be a non-empty menu version for drawing refresh to work

Slider Rules:
  - pfodDevice MUST respond to every slider update message
  - pfodApp waits for response before sending next slider update
  - pfodDevice cannot distinguish intermediate vs final slider position

Touch Resolution:
  - Col/row returned are integers in terms of touchZone's OWN unscaled col/row dimensions
  - Resolution is independent of on-screen scaling

Raw Data:
  - Limited to US-ASCII (0x20 space to 0x7E ~) plus CR and LF
  - Numbers must be in US-ASCII
  - UTF-8 is saved to file but not decoded on display screen

Menu Caching:
  - An update message {; ...} CANNOT change a menu's <version>
  - Menu item TYPES cannot be changed via updates (button/label/nav/dwg are fixed)
  - Cached menus persist across disconnects for same pfodDevice

Input Screens:
  - All input screens dismissed after user submits
  - {} response to input screen: pfodApp re-requests previous menu
  - pfodApp must not return partial UTF-8 byte sequences

Formatting:
  - Background colour <bg ...> applies to whole item/menu, not inside displayString
  - Only one background colour per menu item
  - pfodApp is not required to render text formatting
  - Don't use formatting to convey meaning (use text only as fallback)

---

SECTION 18: WIRE FORMAT REFERENCE TABLE
==========================================

pfodApp -> pfodDevice:
  {@}                          requestCurrentTime (also setLanguage)
  {@~fr~en}                    setLanguage with preference
  {.}                          getMainMenu
  {<cmd>}                      command (no args)
  {<cmd>`<int>}                command with integer arg
  {<cmd>~<str>}                command with string arg
  {<ver>:<cmd>}                refresh/re-request with cached version
  {!}                          closeConnection
  { }                          keepAlive (space inside braces)
  {_}                          secure connection prompt
  {_|<hex>}                    secure connection response

pfodDevice -> pfodApp:
  {}                           empty (ack, dismiss input screen stays on menu)
  {@[~lang][~datetime][`ms]}   current time / language
  {,<fmt>~<title>[`ms][~ver][|items]*}  menu
  {;[bg][~title][`ms][|items]*}         update menu
  {=[title][`max][~opts]|fields*}       streaming raw data / plot
  {'<cmd>[`max][~prompt][|text]}        string input
  {#<cmd>[~prompt]`cur[`max[`min]][~units[~scale[~offset]]]}  numeric input
  {?<cmd>[`init][~prompt]|opt0[|optN]*} single selection
  {*<cmd>[`init]*[~prompt]|opt0[|optN]*} multiple selection
  {!<prompt>}                  close connection with message
  {+[bg]`w`h[~m[`refresh][~ver]]items*}  drawing initial load
  {+[~m]|items*}               drawing update/continuation
  {<}                          go back
  {<<}                         go home
  {_<challenge><hashid>}       secure connection challenge
  {_}                          secure connection accepted

Drawing primitive wire tokens (inside {+...} messages):
  |l   line
  |r   rectangle (unfilled)
  |R   filled rectangle
  |rr  rounded rectangle
  |RR  filled rounded rectangle
  |c   circle (unfilled)
  |C   filled circle
  |a   arc (unfilled)
  |A   filled arc
  |t   text label
  |v   value label (mapped integer)
  |h   hide primitive or touchZone
  |uh  unhide primitive or touchZone
  |e   erase primitive or touchZone
  |hd  hide inserted drawing
  |uhd unhide inserted drawing
  |ed  erase inserted drawing
  |z~col~row~scale  pushZero
  |z              popZero
  |i`idx          index (reserve slot)
  |x   touchZone
  |X   touchAction
  |XI  touchActionInput
  |d   insertDwg

---
END OF SUMMARY
