← back to Dw Photo Capture
feat: Option B — Mac-side sticker watcher (use both). sticker-watcher.js polls FileMaker for web_print_request-flagged records, fires the existing print script via AppleScript do-script (combo-sku param), clears the flag; reuses fm-client.js, loads FM creds from filemaker-mcp/.env. launchd plist staged (not started — needs field + real print-script names). Doc: arm ONLY ONE of A/B (double-print race); DTD favored A, B built as the no-FileMaker-window option.
11691f48f73666631616127c7a26afc9cebd64e3 · 2026-07-06 18:35:46 -0700 · Steve Abrams
Files touched
M FILEMAKER-STICKER-SETUP.mdA launchd/com.steve.dwphoto-sticker-watcher.plistA sticker-watcher.js
Diff
commit 11691f48f73666631616127c7a26afc9cebd64e3
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 6 18:35:46 2026 -0700
feat: Option B — Mac-side sticker watcher (use both). sticker-watcher.js polls FileMaker for web_print_request-flagged records, fires the existing print script via AppleScript do-script (combo-sku param), clears the flag; reuses fm-client.js, loads FM creds from filemaker-mcp/.env. launchd plist staged (not started — needs field + real print-script names). Doc: arm ONLY ONE of A/B (double-print race); DTD favored A, B built as the no-FileMaker-window option.
---
FILEMAKER-STICKER-SETUP.md | 30 +++++++++++-
launchd/com.steve.dwphoto-sticker-watcher.plist | 32 +++++++++++++
sticker-watcher.js | 64 +++++++++++++++++++++++++
3 files changed, 125 insertions(+), 1 deletion(-)
diff --git a/FILEMAKER-STICKER-SETUP.md b/FILEMAKER-STICKER-SETUP.md
index 4a84e07..c89dace 100644
--- a/FILEMAKER-STICKER-SETUP.md
+++ b/FILEMAKER-STICKER-SETUP.md
@@ -56,7 +56,35 @@ on the Mac attached to the Zebra. It'll print + clear each flagged record every
- Verify end-to-end: scan → popup → Print → confirm the flag lands on the record (I can read it back)
and `Date WP Sample Sent` stamps today.
-## Alternative if you don't want a FileMaker script
+--------------------------------------------------------------------------------
+## "USE BOTH" — both print mechanisms are built. ARM ONLY ONE AT A TIME.
+Both A (FileMaker OnTimer poller) and B (Mac watcher) watch the same `web_print_request` flag and
+clear it after printing. **Running both simultaneously double-prints** (race on the flag) — so keep
+one ARMED (auto-printing) and the other as a manual/standby fallback. DTD verdict favored A for
+reliability; B is built for when you'd rather not keep a FileMaker window armed.
+
+### Option B — Mac-side watcher (built, staged, NOT yet started)
+Files: `sticker-watcher.js` + `launchd/com.steve.dwphoto-sticker-watcher.plist`. It runs on the Mac
+by the Zebra, polls FileMaker for flagged records, fires your print script via AppleScript, clears
+the flag. To ACTIVATE (after STEP 1 field exists):
+1. Edit the plist's `FM_PRINT_SCRIPT_SKU` / `FM_PRINT_SCRIPT_ADDR` to your real script names.
+2. Your print script must accept the passed **combo sku** as its parameter and go to that record.
+ If it just prints the current record, add this tiny wrapper script and point the plist at it:
+ ```
+ # Print Flagged Record (param = combo sku)
+ Enter Find Mode [] · Set Field [ WALLPAPER::combo sku ; Get(ScriptParameter) ] · Perform Find []
+ If [ Get(FoundCount) = 1 ] · Perform Script [ "YOUR EXISTING PRINT SCRIPT" ] · End If
+ ```
+3. Grant Automation permission (macOS will prompt: Terminal/node → control "FileMaker Pro").
+4. `cp launchd/com.steve.dwphoto-sticker-watcher.plist ~/Library/LaunchAgents/ &&`
+ `launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.dwphoto-sticker-watcher.plist`
+ (remove: `launchctl bootout gui/$(id -u)/com.steve.dwphoto-sticker-watcher`).
+ Keep FileMaker Pro open on that Mac. Logs: `sticker-watcher.log`.
+
+### To use A instead: don't start the watcher; paste the FileMaker poller (STEP 2 above). Use one.
+
+--------------------------------------------------------------------------------
+## (legacy) zero-scripting alternative
If your Zebra Mac stays open, I can instead build a tiny Mac-side watcher (launchd + AppleScript
`tell application "FileMaker Pro" to do script "<your print script>"`) that polls the flag and fires
your existing print script — no new FileMaker script needed. Tell me your print script's exact name
diff --git a/launchd/com.steve.dwphoto-sticker-watcher.plist b/launchd/com.steve.dwphoto-sticker-watcher.plist
new file mode 100644
index 0000000..325a7cf
--- /dev/null
+++ b/launchd/com.steve.dwphoto-sticker-watcher.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.steve.dwphoto-sticker-watcher</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/opt/homebrew/bin/node</string>
+ <string>/Users/macstudio3/Projects/dw-photo-capture/sticker-watcher.js</string>
+ </array>
+ <key>WorkingDirectory</key>
+ <string>/Users/macstudio3/Projects/dw-photo-capture</string>
+ <key>EnvironmentVariables</key>
+ <dict>
+ <key>HOME</key><string>/Users/macstudio3</string>
+ <key>FM_PRINT_FLAG_FIELD</key><string>web_print_request</string>
+ <!-- Set these to YOUR existing FileMaker print-script names before activating: -->
+ <key>FM_PRINT_SCRIPT_SKU</key><string>PRINT SKU STICKER</string>
+ <key>FM_PRINT_SCRIPT_ADDR</key><string>PRINT ADDRESS STICKER</string>
+ <key>STICKER_POLL_MS</key><string>4000</string>
+ </dict>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>KeepAlive</key>
+ <true/>
+ <key>StandardOutPath</key>
+ <string>/Users/macstudio3/Projects/dw-photo-capture/sticker-watcher.log</string>
+ <key>StandardErrorPath</key>
+ <string>/Users/macstudio3/Projects/dw-photo-capture/sticker-watcher.err.log</string>
+</dict>
+</plist>
diff --git a/sticker-watcher.js b/sticker-watcher.js
new file mode 100644
index 0000000..8abcfd4
--- /dev/null
+++ b/sticker-watcher.js
@@ -0,0 +1,64 @@
+// Option B — Mac-side sticker watcher. Runs on the Mac attached to the Zebra printer (launchd).
+// Every few seconds it asks FileMaker Cloud for records flagged `web_print_request`, triggers the
+// EXISTING FileMaker Pro print script via AppleScript (`do script … with parameter <combo sku>`),
+// then clears the flag so it prints once. Reuses this project's fm-client.js.
+//
+// Requires (env, set by the launchd plist):
+// FM_CLOUD_HOST, FM_CLARIS_EMAIL, FM_CLARIS_PASSWORD, FM_COGNITO_USERPOOL, FM_COGNITO_CLIENTID
+// FM_PRINT_FLAG_FIELD (default web_print_request)
+// FM_PRINT_SCRIPT_SKU (your existing SKU-sticker FileMaker script name)
+// FM_PRINT_SCRIPT_ADDR (your existing address-sticker FileMaker script name)
+// STICKER_POLL_MS (default 4000)
+// NOTE: your print script must accept the passed combo-sku as its script parameter and Go-To that
+// record before printing (a 5-line wrapper is in FILEMAKER-STICKER-SETUP.md if it doesn't already).
+// Do NOT run this AND the FileMaker OnTimer poller (Option A) at the same time — pick one; double-arm double-prints.
+const fs = require('fs'), path = require('path');
+// Load FM_* creds from the master filemaker-mcp .env if not already in the environment (so the
+// launchd plist doesn't have to carry secrets — it only sets the print-script names).
+try {
+ for (const l of fs.readFileSync(path.join(process.env.HOME, 'Projects/filemaker-mcp/.env'), 'utf8').split('\n')) {
+ const m = /^(FM_[A-Z_]+)=(.*)$/.exec(l.trim());
+ if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^["']|["']$/g, '');
+ }
+} catch (e) {}
+const { fmFind, fmSet } = require('./fm-client.js');
+const { execFile } = require('child_process');
+
+const DB = 'WALLPAPER', LAYOUT = 'Sample Requests via email';
+const FLAG = process.env.FM_PRINT_FLAG_FIELD || 'web_print_request';
+const SKU_SCRIPT = process.env.FM_PRINT_SCRIPT_SKU || 'PRINT SKU STICKER';
+const ADDR_SCRIPT = process.env.FM_PRINT_SCRIPT_ADDR || 'PRINT ADDRESS STICKER';
+const INTERVAL = Number(process.env.STICKER_POLL_MS) || 4000;
+
+function log(...a) { console.log(new Date().toISOString(), ...a); }
+
+// Trigger an existing FileMaker Pro script with the record's combo-sku as the parameter.
+function doScript(name, param) {
+ return new Promise((resolve) => {
+ const esc = s => String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
+ const osa = `tell application "FileMaker Pro" to do script "${esc(name)}" with parameter "${esc(param)}"`;
+ execFile('osascript', ['-e', osa], { timeout: 15000 }, (err, so, se) => resolve({ ok: !err, err: err && (se || err.message) }));
+ });
+}
+
+async function tick() {
+ let recs;
+ try { recs = (await fmFind(DB, LAYOUT, { [FLAG]: '*' }, { limit: 20 })).records; }
+ catch (e) { log('poll error:', e.message); return; }
+ for (const r of recs) {
+ const f = r.fieldData || {};
+ const flag = String(f[FLAG] || '').toLowerCase();
+ const key = f['combo sku'] || f['Mfr Pattern'] || r.recordId;
+ const script = flag === 'address' ? ADDR_SCRIPT : SKU_SCRIPT;
+ const out = await doScript(script, key);
+ if (out.ok) {
+ try { await fmSet(DB, LAYOUT, r.recordId, { [FLAG]: '' }); log('printed', flag, key, '(rec ' + r.recordId + ')'); }
+ catch (e) { log('printed but FLAG-clear failed for', r.recordId, e.message); }
+ } else {
+ log('PRINT FAILED for', key, '—', (out.err || '').toString().slice(0, 120));
+ }
+ }
+}
+
+log(`sticker-watcher up · flag=${FLAG} · sku="${SKU_SCRIPT}" addr="${ADDR_SCRIPT}" · every ${INTERVAL}ms`);
+(async function loop() { for (;;) { await tick(); await new Promise(r => setTimeout(r, INTERVAL)); } })();
← 8d68c8e docs: precise 2-step FileMaker setup (create web_print_reque
·
back to Dw Photo Capture
·
feat: visual identify — Gemini compares the ACTUAL user phot 873541c →