23 Commits
Author SHA1 Message Date
grabowski 3ac7cd243b Labels encode just the lamp ID
Internal inventory labels: the QR carries L#### only, which keeps it
at version 1 (large modules, reliable at 23 mm) and means scanning a
label at the station types the ID and lands directly in the
re-measure flow.
2026-07-09 18:16:13 +07:00
grabowski 719ddcf04a Print lamp ID labels on the Brother QL-820NWB
label_printer.py renders a DK-11221 23x23 mm label (QR code linking
to the lamp's buildfor.life page plus the ID) and sends it over the
network backend (brother-ql-next). The station prints two after each
successful measurement, one for the box and one for the lamp; printer
failures never lose a run, and any lamp can be reprinted with
uv run label_printer.py <ID>. Printer address via HPCS_LABEL_PRINTER
or --printer.
2026-07-09 18:15:01 +07:00
grabowski 7ef2960254 Station flow: ID first, EAN optional, re-measure by ID
Every run opens with the freshly allocated L#### and then asks for
the EAN, which Enter skips. Typing an existing ID re-measures that
lamp with all identification and rated values pre-filled, which also
rewrites old bundles in the current metrics format. Unused IDs are
never burned since numbering derives from written bundles.
2026-07-09 18:06:39 +07:00
grabowski 6140e1d4c9 Station no longer scaffolds device pages
The website derives its lamp list from the comparison-data bundles,
so measuring and pushing is the whole publish flow. Dead web-repo
constant removed with it.
2026-07-09 17:09:15 +07:00
grabowski 503fbaa420 Ignore local AI-tooling state directories 2026-07-09 15:44:44 +07:00
grabowski 9fdb12b533 Canonicalize brand names; slim the scaffold to rated specs
Manufacturer input maps to one canonical spelling (seed list plus
whatever the database and cache already use), so Philips is never
philips/PHILIPS. Identity fields no longer duplicate into the device
page specs (the site renders them from metrics.json); the weight scan
ignores index.md so a fresh run starts at weight 1.
2026-07-09 15:38:53 +07:00
grabowski b62eac37c6 Prompt for dimmability; record it in metrics.json
y / n / Enter-if-unknown at the station; only known values are
written, and the scaffolded device page gets a Dimmable spec.
2026-07-09 15:16:46 +07:00
grabowski cf30182efa Update scaffold hint: submodule bump is now automated 2026-07-09 14:49:16 +07:00
grabowski fad8ec0813 Record lamp technology type (led / halogen / cfl / ...)
Station prompts for it (default led), metrics.json carries it as
'type', and the scaffolded device page description and Type spec
follow it.
2026-07-09 14:44:31 +07:00
grabowski 817822594c Add lamp_station.py: barcode-driven measurement workflow
Interactive loop around lamp_export.py: scan an EAN (checksum
validated, optional for barcode-less lamps), resolve it against
previous lamps / a local cache / upcitemdb.com, confirm
identification, record the packaging's advertised values, measure
per the published procedure, and commit the bundle to the
comparison-data repo with a device page stub for the web repo.

Every lamp is keyed by an internal ID (L0001, L0002, ...) so one EAN
can cover several versions (daylight/warm white variants) and
unbranded lamps work. lamp_export.py passes optional ean, variant,
and rated (advertised values) fields through into metrics.json.
2026-07-09 12:13:03 +07:00
grabowski 488c9a399c Add lamp comparison export with TM-30 computation
lamp_export.py writes the per-lamp bundle consumed by the buildfor.life
comparison pages: spd.csv (full 380-1050 nm spectrum), tm30.csv (ANSI/IES
TM-30-18 hue-bin data for the color vector graphic), and metrics.json
(photometric, colorimetric, CRI R1-R15, TM-30 Rf/Rg, electrical, supply
settings).

- tm30.py computes TM-30-18 via colour-science from the measured spectrum,
  resampled to a uniform 1 nm grid; validated against CIE FL2 (Rf 70, Rg 86)
- Optional built-in PSU control (--mode/--voltage/--frequency/--current),
  switched on for the measurement and off afterwards, with --settle warm-up
  and --interval between readings
- Readings use the vendor single-shot cycle verified from USB captures:
  test config -> trigger (8C 0E 02) -> poll -> read -> reset per reading
- Also supports --passive and --parse <pcap> sources
2026-07-08 12:04:18 +07:00
grabowskiandClaude Opus 4.5 cfd589b04b Only auto-control PSU in continuous mode
Single measurements don't toggle PSU - the device handles it.
PSU auto-control now only happens with --continuous flag.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:38:51 +07:00
grabowskiandClaude Opus 4.5 20c61d5cff Use take_single_reading() for single measurements in CLI
Only use take_reading() (continuous-style) when --continuous flag is set.
Default single measurement now matches vendor single-test behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:36:58 +07:00
grabowskiandClaude Opus 4.5 d06bf451f5 Simplify single-test to match vendor capture exactly
Remove send_test_config() call - vendor software doesn't send 8C 2B for
each single test, only when changing modes. Remove set_integration_time()
from spectrometer.py - vendor doesn't send 8C 01 either.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:34:31 +07:00
grabowskiandClaude Opus 4.5 fe9ced5f5b Send test config (8C 2B) before single-shot measurement
The device needs the test config commands to enter single-test mode.
Without them it behaves as continuous start+stop. Supports auto_psu flag
so the device can handle PSU on/off automatically.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 10:04:47 +07:00
grabowskiandClaude Opus 4.5 98101ab5d0 Document single-shot measurement and test config commands (8C 2B, 8C 7B)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 09:29:05 +07:00
grabowskiandClaude Opus 4.5 3bf4a7ad36 Add single-shot measurement mode matching vendor protocol
Stop command triggers a one-shot reading instead of start+poll+stop.
spectrometer.py now uses take_single_reading() for this flow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 09:23:38 +07:00
grabowskiandClaude Opus 4.5 15d8444cac Add calibration.py and force auto integration in spectrometer.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 09:13:07 +07:00
grabowskiandClaude Opus 4.5 739f828dea Remove PSU control from spectrometer.py — machine handles it
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 15:05:59 +07:00
grabowskiandClaude Opus 4.5 85da7f09dc Add delay after PSU on to let it stabilize before measuring
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 15:02:50 +07:00
grabowskiandClaude Opus 4.5 031bb420f9 Add spectrometer.py JSON wrapper for external tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:21:15 +07:00
grabowskiandClaude Opus 4.5 7c00320fe8 Fix unicode multiplication signs in comments
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:00:34 +07:00
grabowskiandClaude Opus 4.5 0f09c70215 first commit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:57:58 +07:00