dashboard for mscc and printer related affects
  • Python 68.6%
  • JavaScript 20.3%
  • CSS 4.9%
  • HTML 4.7%
  • PowerShell 1.4%
Find a file
2026-10-30 05:52:55 -04:00
.hermes/plans Initial HP MSCC dashboard checkpoint 2026-06-23 13:53:20 -04:00
data Initial HP MSCC dashboard checkpoint 2026-06-23 13:53:20 -04:00
docs Add reviewed MSCC site recovery and refresh dashboard interface 2026-10-30 05:47:35 -04:00
extension Add secure onboarding and scoped Windows pilot 2026-08-03 16:43:03 -04:00
packaging/windows Prepare 0.6.0-beta.1 manual Windows build release 2026-10-30 05:52:55 -04:00
scripts Prepare 0.6.0-beta.1 manual Windows build release 2026-10-30 05:52:55 -04:00
site Add reviewed MSCC site recovery and refresh dashboard interface 2026-10-30 05:47:35 -04:00
site-catalog Add secure onboarding and scoped Windows pilot 2026-08-03 16:43:03 -04:00
site-seeds Add secure onboarding and scoped Windows pilot 2026-08-03 16:43:03 -04:00
tests Add reviewed MSCC site recovery and refresh dashboard interface 2026-10-30 05:47:35 -04:00
.gitignore Prepare 0.6.0-beta.1 manual Windows build release 2026-10-30 05:52:55 -04:00
README.md Prepare 0.6.0-beta.1 manual Windows build release 2026-10-30 05:52:55 -04:00
START_HERE.md Add secure onboarding and scoped Windows pilot 2026-08-03 16:43:03 -04:00

MSCC Printer Fleet Tooling

Local tooling for turning MSCC HP printer inventory exports plus authenticated consumable captures into a searchable local page.

Current version: 0.6.0-beta.1. This update adds administrator connection diagnostics, reviewed site recovery with rollback, and the refreshed dashboard layout. Automatic application updates are not implemented in this release.

Quick start

# Start the local server (serves fleet page + renewal page)
python3 scripts/serve.py --port 8766

Then open http://127.0.0.1:8766/

The supported server default is localhost-only (127.0.0.1). It does not require an inbound firewall exception. Binding another interface is retained only for explicit development testing and is not part of the commercial pilot.

The fleet page shows inventory/search immediately from the last built data. Supply status will be Unknown until a live refresh runs.

Connection and site recovery

Administrators can open Connection & site from the dashboard navigation. Use Verify connection to distinguish an imported session from a successful MSCC request. The page identifies expired/rejected sessions, denied access, network failures, invalid responses, empty site results, and recognized site renames.

Refresh the site directory, search for the current name or stable MSCC site ID, and compare a selection before applying it. The preview shows the old/new site and contract, matched printers, additions, and removals. Applying requires an explicit confirmation and another live validation. Existing readings and local placement are retained only for matching devices on the same site ID, contract, and deal type.

Every applied selection saves a private recovery snapshot and audit entry. Restore previous snapshot restores both the selection and its saved fleet; it also snapshots the state being replaced. Recovery snapshots survive normal generation cleanup. They contain no session credentials and remain local under data/recovery/. See the recovery workflow.

Windows executable pilot

The PyInstaller one-folder pilot includes Python and all runtime dependencies, defaults unambiguously to localhost, opens the local browser, and publishes the unpacked extension under the current user's LocalAppData. It contains no session, administrator credential, invite state, shipment history, or raw MSCC capture. The pilot includes a validated, credential-free catalog of 168 MSCC sites. Administrators search that saved list, refresh it from MSCC when a site is missing, and retrieve printer rows only for the selected site. No Python installation is needed on the OSA workstation.

Build and clean-machine test instructions are in docs/windows-executable-pilot.md.

Day-to-day refresh

python3 scripts/mscc_refresh.py

If the session token is still valid: refreshes the fleet list from MSCC, reads each stored printer IP directly when reachable, falls back to MSCC per printer, then rebuilds the fleet page.

If the token is expired or missing: the script starts the local server (if not already running), opens your browser to a renewal page, and waits. With the optional extension pilot installed, open MSCC and complete normal sign-in/PingID only when prompted; the renewed session is forwarded automatically. The DevTools snippet and JSON upload remain available as fallbacks.

Architecture

mscc_refresh.py          ← one-command entry point
  ├─ serve.py            ← local HTTP server (static + API + CORS)
  ├─ mscc_session_store.py ← protects the full MSCC session (Windows DPAPI)
  ├─ scoped_refresh.py    ← enforces SiteScope and commits private generations
  ├─ site_recovery.py     ← connection checks, selection previews, audit and rollback
  ├─ mscc_site_catalog.py ← captures and validates the dedicated MSCC site list
  ├─ site_seed_catalog.py ← validates packaged site snapshots for fast onboarding
  ├─ mscc_live_refresh.py ← scoped MSCC client and direct-first device capture
  └─ build_site_data.py   ← validates and builds a staged generation payload

installation_config.py    ← validates and atomically stores installation scope metadata
mscc_supply_shipments.py  ← refreshes supply-order tracking history
printer_ews.py             ← reads a fixed supplies page directly from a printer IP
find_underused_printers.py ← builds a field-review CSV from capture history
extension/                 ← optional Edge/Chrome session bridge pilot
packaging/windows/          ← self-contained Windows executable build
site-seeds/                 ← allowlisted credential-free onboarding snapshots
site-catalog/               ← packaged site rows only; no printer records or session

site/
  ├─ index.html           ← fleet dashboard (search, filter, detail panel)
  ├─ connection.html      ← administrator connection and site recovery
  ├─ renew.html           ← session renewal page (snippet copy + auto-detect)
  ├─ auth.html            ← first-run administrator setup and login page
  ├─ review.html          ← shared operator-invitation claim page
  ├─ leadership-access.html ← administrator invite and claimed-user controls
  ├─ app.js               ← fleet page logic
  ├─ auth.js              ← setup/login behavior
  ├─ review.js            ← individual operator-session claim flow
  └─ styles.css            ← styling; app.js loads authenticated /api/fleet

Pieces

  • scripts/serve.py serves the fleet page, renewal page, and API routes (session receive, snippet, health). Replaces python3 -m http.server.
  • scripts/dashboard_auth.py stores one local administrator using a salted password hash and manages expiring login sessions.
  • scripts/installation_config.py validates versioned installation/site metadata and writes it atomically. It never stores bearer, password, printer, or licensing data.
  • scripts/mscc_session_store.py stores the complete MSCC session behind an injectable protection backend. Windows source and packaged runs use CurrentUser DPAPI; see docs/session-storage-migration.md.
  • scripts/mscc_refresh.py is the supported command-line refresh. It requires completed onboarding, reads the protected session and configured SiteScope, and commits a private inventory generation.
  • scripts/scoped_refresh.py owns authenticated fleet and per-printer refresh transactions. It rejects session/scope mismatches and never writes the legacy public fleet file.
  • scripts/mscc_site_catalog.py reads MSCC's dedicated paginated site directory with a stable site-name sort, removes duplicate rows, validates the credential-free packaged/local catalog, and atomically preserves administrator rescans.
  • scripts/site_seed_catalog.py validates packaged site snapshots, strips shipment/runtime state, and exposes only sanitized site summaries before selection. A current matching MSCC session must authorize the exact scope before activation.
  • scripts/build_site_seed.py deduplicates immutable generations by site fingerprint and emits the newest active credential-free seed for a future release.
  • scripts/mscc_live_refresh.py refreshes the fleet list through MSCC, then tries each stored printer IP before using MSCC details and consumables as the per-device fallback. Devices are processed concurrently, so one unreachable printer does not stop the fleet refresh.
  • scripts/build_site_data.py validates capture, shipment, and inventory scope before producing a generation-local fleet payload. Its old standalone output mode is development-only.
  • scripts/mscc_supply_shipments.py refreshes supply-order history only from the already validated selected-site row set during the supported refresh.
  • scripts/printer_ews.py performs a read-only direct supplies-page GET against a literal printer IP. Both fleet and per-printer dashboard refreshes prefer this current device data and fall back to MSCC when direct access is unavailable.
  • scripts/find_underused_printers.py scores historical toner movement and writes a manual-review CSV.
  • extension/ contains the optional Manifest V3 pilot that forwards a valid MSCC browser session to the local server after normal sign-in/PingID authentication.
  • scripts/mscc_export_session_snippet.js browser-console snippet. Extracts MSCC session from localStorage and POSTs it to the local server (falls back to file download if server is not running).
  • scripts/inspect_mscc_export.py inspects CSV/XLSX exports without printing raw rows.
  • scripts/mscc_capture_consumables_snippet.js browser-console snippet for per-device consumable levels (standalone flow).
  • scripts/mscc_collect_filtered_device_rows_snippet.js lighter snippet: paginate filtered device list, download rows + detail targets.
  • scripts/mscc_capture_consumables_from_stored_list_snippet.js consumable capture from a stored device list (skips list pagination).
  • scripts/mscc_save_page_snapshot_snippet.js downloads a sanitized snapshot of the current MSCC page.

Browser extension session bridge (pilot)

The optional Edge/Chrome extension removes the DevTools copy/paste step. It does not bypass authentication or read browser cookies, passwords, or PingID responses. After MSCC finishes its normal authentication, the extension reads the same short-lived bearer token and customer context as the existing snippet and forwards them to http://127.0.0.1:8766/api/session.

See docs/browser-extension-pilot.md for one-time installation, testing, and removal instructions. The extension and dashboard server must run on the same computer. The manual snippet and JSON upload remain available as fallbacks.

Do not repoint the current session bridge at an internet URL. The current server can read the saved bearer token, so dashboard password protection alone does not isolate that credential from a server-side compromise. See docs/remote-deployment-security.md for the safe remote choices: a sanitized extension capture upload (preferred) or a separate private credential broker.

Session renewal

The renewal flow replaces the old 7-step manual process:

Old way: Log into MSCC → DevTools → paste snippet → download file → move file to data/ → rerun script.

New way: Run mscc_refresh.py. If the token is expired:

  1. The browser opens http://127.0.0.1:8766/renew.
  2. Open MSCC in the normal browser profile where the pilot extension is installed.
  3. Complete normal sign-in/PingID only when HP requests it.
  4. The extension sends the renewed session and the script continues on its own.

The renewal page retains the browser-console snippet and file-upload fallbacks.

Dashboard login and first setup

The dashboard requires a local administrator account. On the first run, open http://127.0.0.1:8766/setup directly on the dashboard computer and create a username plus a password of at least 12 characters. First setup is deliberately blocked through Cloudflare Tunnel so a public visitor cannot claim the initial administrator account.

After setup, /, /renew, /api/fleet, refresh controls, and operational status routes require login. The health check and the origin-restricted MSCC browser-extension/session handoff remain available so local health monitoring and token renewal continue to work. The credential file is local, ignored by Git, and never included in the Windows package.

An administrator can use Leadership access to create a shared link with a configurable claim limit and claim window. Each successful browser claim gets its own persistent operator session, optional display name, claim address, and administrator-managed pause/revoke controls. Operators can run fleet and printer refreshes and open MSCC device links, but cannot renew or replace the installation-wide MSCC session, change onboarding, open server logs, or manage access.

Start the configured local dashboard

python3 scripts/serve.py --port 8766

Then open http://127.0.0.1:8766/

Authenticated site-scoped refresh

Complete onboarding and import the MSCC session through the extension, snippet, or JSON-upload flow. Then use the dashboard's Refresh from MSCC control or run:

python3 scripts/mscc_refresh.py

The backend loads customer, contract, exact site, and deal scope from the validated installation configuration. Browser and production CLI requests cannot override it. Direct mscc_live_refresh.py capture-file use requires the explicit --development-override flag and is not a production workflow.

Use Sync printer list for a faster administrator-only membership check. It adds newly listed printers and removes printers no longer returned for the exact configured site without retrieving details, supplies, or shipments. Existing printer observations and placement fields are preserved; new printers begin with unknown supplies until a direct or full MSCC refresh.

The refresh stores MSCC's current ipAddress for every device detail response and reports withIpAddress coverage in the capture totals. If MSCC returns a blank/null address, the dashboard leaves it blank instead of retaining a stale address from the inventory export.

Capture low-toner / consumable levels from MSCC

MSCC does not include toner levels in the change-file export. The printer detail page calls a consumables API.

Discovered endpoints:

  • Device list: POST https://print.services.api.hp.com/ms-dcc/svc/ms-dcc/deviceListByCustomerId
  • Device details: GET https://print.services.api.hp.com/ms-dcc/svc/ms-dcc/getItemDetailsByItemId
  • Consumables (via redirect): POST https://print.services.api.hp.com/ms-dcc/svc/ms-dcc/redirectAPI with msName: ms-supply-order and apiEndpoint: /getConsumableLevels

Legacy standalone capture flow (development diagnostics only):

  1. Log into https://mscc.ext.hp.com/us/en/.
  2. Select the target customer/site, open the /devices page.
  3. Open DevTools Console.
  4. Paste scripts/mscc_capture_consumables_snippet.js.
  5. Wait for it to finish. It downloads a JSON file.
  6. Move the JSON into data/.
  7. Rebuild:
python3 scripts/build_site_data.py --development-override --capture data/mscc-consumables-capture-<timestamp>.json --location-name "<sanitized-location>" --only-captured

The --only-captured flag shows only printers from the location-filtered capture instead of the full export.

Discover ODP Business order tracking

ODP Business order tracking appears to be login-gated under /orderhistory. Use a browser capture first; do not put credentials in scripts.

Safe discovery flow:

  1. Log into https://www.odpbusiness.com/ normally in your browser.
  2. Open https://www.odpbusiness.com/orderhistory/default.do or the Orders page.
  3. Open DevTools Console.
  4. Paste scripts/odp_orderhistory_intercept_snippet.js.
  5. Open the order history list, click an order, and open tracking/details.
  6. Click Stop & Download. The downloaded odp-orderhistory-intercept-*.json file is gitignored.

The capture filters for order-history/tracking calls and redacts obvious auth/session fields, but it can still contain internal order numbers, delivery data, and account details. Treat it as local sensitive data.

Add shipment data to the dashboard

The dashboard can merge a local shipment/order JSON file into printer details. If data/shipments.json exists, normal live refresh rebuilds merge it automatically. You can also pass a specific file:

python3 scripts/build_site_data.py \
  --capture data/mscc-consumables-live-<timestamp>.json \
  --shipments data/shipments.json \
  --only-captured

Shipment records match printers by any of these fields: serialNumber, serial_number, printerSerial, deviceSerial, assetNumber, asset_number, hostName, host_name, deviceId, or device_id.

Minimal record shape:

{
  "items": [
    {
      "serialNumber": "JPBCV140RY",
      "orderNumber": "SO-100",
      "trackingNumber": "1Z999",
      "carrier": "ODP",
      "status": "In transit",
      "eta": "2026-06-25",
      "items": [
        { "description": "Cyan Toner Cartridge", "quantity": 1 },
        { "description": "Black Toner Cartridge", "quantity": 1 }
      ]
    }
  ]
}

The UI shows matched shipments in each printer detail panel and adds a shipment filter for Incoming, Received, and None.

Shipment history is refreshed as part of the same site-scoped generation transaction:

python3 scripts/mscc_refresh.py

Shipment queries reuse the validated selected-site device list. A shipment-only failure preserves the prior selected-site shipment snapshot, marks it stale, and does not discard a successful fleet refresh.

Generate an underused-printer field list

Use historical live-capture files to identify printers that merit an on-site donor/underuse review:

python3 scripts/find_underused_printers.py

By default this uses the configured active inventory generation with historical data/mscc-consumables-live-*.json captures, limits the report to groups containing PLANT, and writes data/underused_printers.csv. The report is a field-review aid: current low/unknown supplies, sparse history, stale collection data, stale capture history, and recent low events prevent automatic candidate promotion.

The CSV now separates two kinds of recency. last_collection_date and days_since_collection come from the printer's MSCC detail record; they mean the last successful telemetry collection, not the last time someone printed. last_observed and capture_age_days describe when this report last received usable toner levels. It also includes current minimum toner, capture sample count, history span, low-event count, total toner movement, movement per history day, and movement by toner color. MSCC does not provide page-count usage in this report, so toner movement is a proxy for underuse rather than a proof of zero printing.

Read supplies directly from a printer

When the dashboard computer can reach the printer network, refreshes first read the HP Embedded Web Server supplies page at each printer's inventory IP. The main Refresh from MSCC flow still uses MSCC for fleet discovery, but each printer with a stored IP gets a short direct attempt before its MSCC fallback. The workers continue independently when an IP is unreachable. The per-printer refresh can complete without an MSCC session when its direct read succeeds.

Direct reads capture percentage/status values and, when the printer reports them, installed part numbers, estimated pages remaining, supply serials, page counts, and install/use dates. If a direct page is unreachable or uses an unsupported layout, the existing MSCC refresh remains the fallback.

You can test one address from the terminal:

python3 scripts/printer_ews.py 192.0.2.25

Replace 192.0.2.25 with an IP already listed for that printer. The reader:

  • accepts only a literal IP from the inventory flow;
  • sends a fixed, read-only GET to /hp/device/InternalPages/Index?id=SuppliesStatus;
  • follows no redirects and sends no cookies or credentials;
  • prefers HTTPS, with HTTP fallback for older printer firmware;
  • limits time and response size so an unresponsive device cannot hang the dashboard.

Printer HTTPS certificates are commonly self-signed. Certificate verification is therefore disabled only for this constrained IP/fixed-path request; no password or session material is transmitted.

What the capture contains

The snippet reads the logged-in browser's MSCC auth token only to make the same API calls the MSCC UI makes. It does not write the token, cookies, or full localStorage into the downloaded file.

The JSON contains: context IDs, device-list rows, device-detail summaries, consumable API responses (level/threshold/description), and per-device errors. Treat it as internal operational data — it can include printer identifiers, hostnames, IPs, locations, and consumable levels.

Verification

for script in site/*.js scripts/*.js extension/*.js; do node --check "$script"; done
python3 -m py_compile scripts/*.py
python3 -m unittest discover -s tests -v