This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Matter.js-based controller with a Python Matter Server compatible WebSocket interface, designed for Home Assistant integration. The server listens on localhost:5580/ws and provides a WebSocket API compatible with the Home Assistant Matter integration.
# Install dependencies and initial build
npm i
# Build all packages (incremental)
npm run build
# Clean build (full rebuild)
npm run build-clean
# Start the server
npm run server
# Start server with options
npm run server -- --storage-path data --primary-interface en0
# Run tests (uses @matter/testing with mocha).
# Integration tests (Commission On Network) need an explicit network interface,
# otherwise mDNS discovery picks up VPN/utun adapters and the test times out.
# Set both env vars to the active LAN interface (e.g. en0):
PRIMARY_INTERFACE=en0 MATTER_MDNS_NETWORKINTERFACE=en0 npm test
# Lint (oxlint with type-aware checking)
npm run lint
npm run lint-fix
# Format (oxfmt)
npm run format
npm run format-verifyThis is an npm workspaces monorepo with four packages:
- packages/ws-controller (
@matter-server/ws-controller): Core Matter controller library wrapping@project-chip/matter.js. ExportsMatterController,ControllerCommandHandler,WebSocketControllerHandler,ConfigStorage - packages/dashboard (
@matter-server/dashboard): Web UI built with Lit, Rollup, and Material Web Components. Connects to server via WebSocket - packages/matter-server (
matter-server): Main entry point. HTTP/WebSocket server using Express, combines controller + dashboard - Build tooling is provided by the external
@nacho-iot/js-toolsdev dependency (CLI binariesnacho-build,nacho-run).
MatterServer.ts → creates ConfigStorage + MatterController → creates WebServer with handlers:
WebSocketControllerHandler: Python Matter Server compatible WS API on/wsStaticFileHandler: Serves dashboard assets
The server implements a protocol compatible with Home Assistant's Python Matter Server. Key commands:
start_listening,commission_with_code,device_command,read_attribute,write_attribute- Events:
node_added,node_updated,node_removed,attribute_updated
@project-chip/matter.js: Core Matter protocol implementation@matter/main,@matter/main/general: Matter.js utilities@matter/nodejs-ble: Optional BLE support (enable with--bleflag)
Uses external @nacho-iot/js-tools build system:
- TSC for type checking and declaration files
- esbuild for transpilation (ESM + CJS)
- Dashboard uses Rollup for bundling with Babel
- Shared tsconfigs live in
/tscat repo root (tsconfig.base.json,tsconfig.lib.json,tsconfig.test.json,tsconfig.app.json)
Package-level builds: nacho-build (aliased in each package.json)
Dashboard has additional npm run generate step for cluster descriptions.
Engine requirement: >=20.19.0 <22.0.0 || >=22.13.0
- Linter: oxlint with type-aware checking (config:
.oxlintrc.json) - Formatter: oxfmt (config:
.oxfmtrc.json) - Both are Rust-based and run in <1s across the entire monorepo
Always run these checks before considering work complete:
# 1. Format code (required - MUST run first, oxfmt rewrites files in-place)
npm run format
# 2. Lint (required)
npm run lint
# 3. Build (required)
npm run build
# 4. Run tests (required). See note above — integration tests need both
# PRIMARY_INTERFACE and MATTER_MDNS_NETWORKINTERFACE set to the active LAN
# interface (e.g. en0). Without them, the Commission On Network test times out
# when mDNS picks a VPN/utun interface.
PRIMARY_INTERFACE=en0 MATTER_MDNS_NETWORKINTERFACE=en0 npm testAll four checks must pass in this order. npm run format must be run before build/lint — it rewrites files in-place using oxfmt and the build/lint must validate the formatted output. Skipping format leads to formatting drift that gets caught later.
Plan/design documents in docs/plans/ are working files only. Never commit them to git. They may exist locally for reference but must not be included in any commit.
- Lit 3.x: Web components framework with TypeScript decorators
- Material Web 2.4.x: Material Design 3 components (
md-*elements) - @mdi/js: Material Design Icons as SVG paths
- CSS Variables: All colors use CSS custom properties for theming
- All components use inline
static override styles = css\...`` (Lit pattern) - Colors must use CSS variables from
public/index.html, not hardcoded values - Key variables:
--md-sys-color-primary,--md-sys-color-surface,--md-sys-color-on-surface,--md-sys-color-on-surface-variant - Dark mode: Variables overridden in
html.dark-theme bodyselector
When defining colors in dashboard components, always use CSS variables that are defined for both light and dark themes:
/* CORRECT - uses theme variable with fallback */
color: var(--text-color, rgba(0, 0, 0, 0.6));
background: var(--md-sys-color-surface);
/* WRONG - hardcoded color won't adapt to dark mode */
color: rgba(0, 0, 0, 0.6);
color: #333333;
color: grey;Available theme variables defined in public/index.html:
--text-color: Secondary text (grey) - adapts for light/dark--danger-color: Error/warning red--primary-color: Primary accent color--md-sys-color-*: Material Design system colors
Always verify new colors look correct in both light AND dark themes before committing.
src/util/theme-service.ts: Singleton managing theme state- Supports:
light,dark,system(OS auto-detect) - Persisted in localStorage (
matterThemekey) - Query parameter override:
?theme=dark|light|system
public/index.html: CSS variables for light/dark themessrc/util/theme-service.ts: Theme management singletonsrc/pages/components/header.ts: Header bar with theme togglesrc/pages/matter-dashboard-app.ts: Main app with connection statessrc/components/ha-svg-icon.ts: Custom SVG icon component
- Don't use hardcoded colors like
#673ab7orcornsilk- use CSS variables - Material Web components need proper
--md-sys-color-*variables to render correctly in dark mode - When adding status/error pages, include the header component for consistent UX
- The dashboard is served by the Matter Server, so
location.reload()fails when server is offline - use WebSocket reconnect instead
Two distinct audiences served equally:
- Home Assistant power users — technically capable smart home enthusiasts managing their Matter network. Need clear device status, easy commissioning, and network health at a glance.
- Developers and testers — building or debugging Matter integrations. Need raw data, attribute inspection, cluster details, and diagnostic information.
Both audiences benefit from information density. Neither needs hand-holding.
Utilitarian, dense, reliable. Tool-first. The dashboard should feel like a well-built instrument panel — everything needed visible, nothing decorative for its own sake.
- Material Design 3 as foundation — refine rather than replace
- Both light and dark modes, equal quality in each
- Current design is acceptable baseline — improvements focus on polish, color mode consistency, spacing, and information hierarchy
- Anti-references: not a "hacker dashboard", not overly decorative, not consumer marketing
- Density over decoration — Show more data in less space. Every pixel earns its place.
- State clarity — Online/offline, connection quality, commissioning status must be instantly readable. Color-code consistently.
- Both modes, equal quality — Light and dark themes both intentional, not one an afterthought.
- Material as infrastructure — Use MD components for consistency, override tokens where it improves the tool feel.
- Progressive detail — Overview first, drill-down on demand.