Commit Graph

6 Commits

Author SHA1 Message Date
type-two
d4b16c075b One-command install + launchd agent for deploying to another Mac
Answers "do we have to start the server in terminal?" -- no. install.sh installs
deps and registers a launchd agent with RunAtLoad + KeepAlive, so the daemon
starts at login and comes back if it dies. Verified by killing the process: back
up and serving within seconds under a new pid. Deploying to the M3 Air is then:

    git clone ssh://git@100.71.119.27:222/monster/yourovo.git
    cd yourovo/mac && ./install.sh

Also revises the A/B/C recommendation. Grepping the extension shows it calls only
six endpoints: /status, /write-tag, /read-tag (all served here) plus /weight,
/scale/start, /scale/stop (the DYMO M10 postal scale, Node-only). So the gap to a
single service is the scale and nothing else -- which makes option B (Node on
:7791 with this one proxying) the wrong trade: one URL but two processes, two
dependency stacks and two things to keep alive, against a stated goal of one
button on a second machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:49:21 +10:00
type-two
6d9a49ea42 Fix the EPC scheme; verify encode independently with the Chafon
The SKU<->EPC layout inherited from daemon.cs was WRONG. daemon.cs packs 6 bytes
of SKU + 4 bytes of release_id + an 0xEC01 marker, and flagged itself as
unverified. The shop's actual scheme -- in pliceclogs/rfid-daemon/index.js
(buildEpcPayload/parseEpcData), which is what the extension and every existing
tag use -- is a single 96-bit big-endian integer:

    value = sku(14 digits) * 10^9 + releaseId

Checked against that file's own worked example: 20260321001001 + 35332 ->
20260321001001000035332. epc_decode also reproduces its guard, returning None
when value/10^9 exceeds 14 digits, so a factory tag (E28069...) can never be
reported to the operator as a real SKU.

This matters: parseEpcData rejects the EC01 layout outright, so every tag the
Windows daemon has written is invisible to the extension. ASSESSMENT.md now
carries that as a red open item against daemon.cs.

Verified end-to-end with an INDEPENDENT reader. ASSESSMENT.md sec.4 parked the
Chafon as HID-keyboard-only, but that is Windows-specific -- on macOS it exposes
a CH340 bridge and its documented serial protocol works. New chafon.py (read-only:
inventory + read EPC, CRC-16/0x8408 framing) scans the field and sees:

    0000044A5600D4CDA1FAB932  -> sku=20260722180000 rel=424242   (Urovo-written)
    E28069950000600625F600D3  -> factory/blank  (x12, rest of roll)
    126D5125FFA000067932EC01  -> factory/blank  (earlier EC01 tag, now rejected)

That last line is the bug demonstrated: a tag written earlier today under the old
scheme is unrecognised, exactly as the extension would have treated it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:45:07 +10:00
type-two
d254d16278 Fix Dymo label geometry: rotation and 300x600 dpi halving
Two bugs found on the bench, both from wrong assumptions about the LW450:

1. Orientation. I assumed the 57mm head meant the label feeds landscape. It
   doesn't -- the Dymo takes the SAME 24mm narrow-edge-first stock as the Urovo,
   so the landscape design needs the identical 90-degree rotation. Rendering
   656 dots onto a ~288-dot label also silently clipped the price and QR off the
   right-hand edge. Now rotates by default, overridable per request via "rotate",
   and the daemon rejects an image wider than the head instead of clipping it.

2. Half-length labels. print_image() sent ESC i (300x600 Barcode/Graphics mode),
   where the head stays 300 dpi across but each raster line becomes 1/600" in the
   travel direction -- so a 650-line label printed at 27.5mm instead of 55mm, with
   the across-head dimension correct. Default is now ESC h (300x300), matching our
   square 300 dpi bitmap. graphics_mode=True remains available and now stretches
   the image 2x vertically so physical size stays correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:39:58 +10:00
type-two
2727545ac7 Add Dymo LabelWriter 450 driver; unify both printers in one daemon
Answers "can we direct print to the Dymo via CUPS, or is it the cmd-P dance?":
neither. The LW450 (0922:0020) is a bidirectional USB printer-class device just
like the Urovo, so its raster protocol goes straight to the bulk endpoint -- no
DYMO driver, no CUPS queue, no print dialog. Command set taken from DYMO's own
LabelWriter 450 Series Technical Reference Manual and documented in dymo.py.
Verified on the bench: revision 1750111r53, status 0x03 Ready, 283 raster lines
at 82 bytes/line accepted.

Also corrects an assumption inherited from daemon.cs: UHF WRITE commits
immediately and does NOT wait for PRINT. Confirmed by writing a chip and reading
it back with no PRINT sent. Two consequences:
  - the Urovo can program a chip without printing or consuming a label, which is
    what makes the Urovo-encodes/Dymo-prints split work at all;
  - an encode can be verified BEFORE printing, so a failed write never costs a
    label. /print-encode and /encode both do this now.

New routes: /devices (all three devices at a glance), /encode (chip only),
/print-paper (Dymo), /tag-and-print (Mode A in one call).

label.py is now resolution-aware (203 dpi Urovo / 300 dpi Dymo via profile(dpmm=))
and orientation-aware: the Urovo's 24mm web feeds narrow-edge-first so the design
rotates 90 degrees, while the Dymo's 57mm head is wider than the label so the same
design feeds landscape unrotated.

Also fixes tag read parsing: with dataFormat 'H' the printer replies in ASCII hex
TEXT, not raw bytes -- the old code hex-encoded it a second time. flush_in() now
drains fully, since a late reply to one command was being attributed to the next
and silently corrupting reads.

KNOWN ISSUE: the Node rfid-daemon (Chafon + scale + inventory) also binds :7790.
Both cannot run at once and this service does not implement its routes. Unresolved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:36:34 +10:00
type-two
b014205a5b macOS: raw-TSPL Urovo daemon — no vendor SDK needed
ASSESSMENT.md §7 assumed the RFID encode was locked inside the Windows-only
GTSPL_SDK.dll and would need a USBPcap sniff or the GTSPL Java jar to recover.
It didn't. The DLL is a managed .NET assembly, and its IL shows every RFID call
is a one-line String.Concat -> ASCII -> WritePrinter. Recovered from ldstr order
plus argument order, and confirmed by a second independent decompile:

    UHF WRITE H,2,12,E,"<24 hex>"    UHF READ/QUERY ...
    UHF GEN2 EPC|TID|USER|ACCESS|KILL <action>,"<pw>"
    SET RFID <tagType>,<rw_pos>,<void>,<try>,<err>,<speed>,<retry>   (dots, not mm)
    &DEFAULT,i    &CALIBRATE,A,R    PRINT <set>, <copy>

So the whole recipe is plain ASCII over the printer's bulk endpoint and ports to
macOS with no vendor binaries at all.

- mac/urovo.py   USB transport + full GTSPL command set, status decoding, the
                 ribbon-latch recovery, SKU<->EPC packing ported from daemon.cs.
- mac/label.py   PIL bitmap renderer -> TSPL BITMAP. Media-profile driven, so both
                 shop stocks work (small 51x19 and large 55x24 on the 24mm web),
                 with a safe-area inset so the design can't clip at the edges.
- mac/daemon.py  :7790 HTTP service, same routes/JSON/CORS as daemon.cs, plus a
                 /preview route that renders a label as PNG without printing one.

Verified on the bench from macOS: printer identifies (MODEL:UROVO-D812R), the
latched 0x08 "out of ribbon" clears with SET RIBBON OFF + FORMFEED, and labels
print correctly oriented and dark on the 24x55 stock.

NOT yet verified: the RFID encode itself. It is wired and the printer accepts the
command, but the loaded thermal stock has no inlays so nothing has been written to
a chip. Needs a PET RFID label at the antenna. The SKU<->EPC byte layout also
remains unconfirmed against existing Chafon-written tags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:07:12 +10:00
3056fd70fc Windows Urovo D812R+ print+encode daemon + full assessment
- pricegod-urovo-daemon: C# :7790 HTTP service, drives the Urovo D812R+ via
  the GTSPL SDK; prints a label AND encodes the UHF chip in one pass. Drop-in
  for the PriceGod extension's daemon.js (same :7790, CORS *).
- chafcheck: Chafon H-102 UHFPrimeReader P/Invoke probe.
- ASSESSMENT.md: everything done + learned this session -- the ribbon-latch
  fix, PET-labels-are-not-direct-thermal finding, unreliable-status-byte
  quirk, SKU->EPC scheme, the Chafon HID-mode dead end, and how to port the
  print+encode recipe to the Mac (GTSPL Java SDK / raw TSPL).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 15:18:39 +10:00