# desklock — one entry point for a three-component repo. # # firmware/ ESP-IDF application for the device. No tests, no release flow. # gateway/ Python service on :8600. The only component with a test suite. # sim/ a static page that mimics the device face in a browser. # # This lives at the root and the components have no Makefiles of their own, so # `make test` means the same thing wherever you are standing. A per-component # Makefile makes it mean "some of the tests" depending on your working # directory, which is the `git -C` failure in another costume (D-27). # # Paths resolve here rather than in callers (D-10). Two of them bite: # # ESP-IDF is invisible until export.sh is sourced, so `idf.py` is # "command not found" for anyone who forgets — the same class of failure as # the four tool-resolution misses recorded in D-24. The firmware targets # source it themselves. # # `python3` on this host is 3.8, which cannot parse the gateway's sources. # PYTHON names 3.12 explicitly and is overridable for other machines. PYTHON ?= python3.12 IDF_EXPORT ?= $(HOME)/esp-idf/export.sh GATEWAY := $(CURDIR)/gateway VENV := $(GATEWAY)/.venv .DEFAULT_GOAL := help .PHONY: help help: ## Show this help @grep -hE '^[a-z][a-z0-9_-]*:.*?## ' $(MAKEFILE_LIST) \ | awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}' # --- the three D-27 required targets ----------------------------------------- .PHONY: test test: test-gateway ## Run every component's tests that exist @echo " -- firmware: no test suite (undetermined, not passing)" @echo " -- sim: a static page, nothing to test" .PHONY: lint lint: lint-gateway ## Lint every component that has a linter # --- gateway ------------------------------------------------------------------ .PHONY: setup setup: ## Create the gateway venv and install dev deps (no ML models) cd $(GATEWAY) && $(PYTHON) -m venv .venv && .venv/bin/pip install -e ".[dev]" .PHONY: setup-speech setup-speech: ## Additionally install faster-whisper and piper cd $(GATEWAY) && .venv/bin/pip install -e ".[dev,speech]" .PHONY: run run: ## Run the gateway on :8600 with reload cd $(GATEWAY) && .venv/bin/uvicorn desklock_gateway.main:app --host 0.0.0.0 --port 8600 --reload .PHONY: test-gateway test-gateway: ## Gateway pytest suite @cd $(GATEWAY) && .venv/bin/pytest .PHONY: lint-gateway lint-gateway: ## ruff check and format --check over the gateway cd $(GATEWAY) && .venv/bin/ruff check src tests && .venv/bin/ruff format --check src tests .PHONY: typecheck typecheck: ## mypy over the gateway sources cd $(GATEWAY) && .venv/bin/mypy src # --- firmware ----------------------------------------------------------------- # # Each target sources export.sh in its own shell. That is deliberate: make runs # every recipe line in a fresh shell, so exporting in one target would not carry # to the next, and a caller who sources it by hand still works because sourcing # twice is harmless. .PHONY: build-firmware build-firmware: ## Build the ESP-IDF firmware (sources export.sh for you) @test -f $(IDF_EXPORT) || { echo "FAIL — no ESP-IDF at $(IDF_EXPORT); set IDF_EXPORT=/export.sh"; exit 1; } . $(IDF_EXPORT) && cd firmware && idf.py build .PHONY: flash flash: ## Flash and monitor the device (PORT=/dev/ttyACM0 by default) @test -f $(IDF_EXPORT) || { echo "FAIL — no ESP-IDF at $(IDF_EXPORT); set IDF_EXPORT=/export.sh"; exit 1; } . $(IDF_EXPORT) && cd firmware && idf.py -p $(or $(PORT),/dev/ttyACM0) flash monitor # --- sim ---------------------------------------------------------------------- .PHONY: serve-sim serve-sim: ## Serve the face simulator on :8601 cd sim/face && $(PYTHON) -m http.server 8601 # --- housekeeping ------------------------------------------------------------- .PHONY: clean clean: ## Remove the gateway venv and caches rm -rf $(VENV) $(GATEWAY)/.pytest_cache $(GATEWAY)/.ruff_cache $(GATEWAY)/.mypy_cache find $(GATEWAY) -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true