Git
Get it fromhttps://git-scm.com
Check installation
git --versionPython >=3.11,<3.14
Get it fromhttps://python.org
On Windows checkAdd Python to PATH
Check installation
python --versionThe backend declares this in svc/pyproject.toml. The repo-local svc/.python-version is for tools that read it.
UV(Recommended package manager)
Get it fromhttps://github.com/astral-sh/uv
Installation (one-liner):
curl -LsSf https://astral.sh/uv/install.sh | shOr use pip: pip install uv
Or use homebrew: brew install uv
Check installation
uv --versionNode.js (LTS) and NPM
Get it fromhttps://nodejs.org
Check installation
node --version
npm --version- Clone the repository and go inside
cd GlazingControlApp- Open a terminal in the
svcfolder
cd svc- Install dependencies and sync virtual environment
UV will automatically create and manage a virtual environment:
uv sync- Create your
.envfile from the example
3.1 Windows
copy .env.example .env3.2 Mac/Linux
cp .env.example .env- Start the server
uv run python main.pyOr activate the virtual environment first:
source .venv/bin/activate # Mac/Linux
# or
.venv\Scripts\activate # Windows
python main.pyYou should see Uvicorn running on port 8000
Open the API docs in a browser at http://127.0.0.1:8000/docs
Use this on the site computer after svc/data/sensors_config.json has the real sensor values:
cd svc
$env:SVC_MODE = "real"
uv sync
uv run python main.pyFor the EKO C-BOX, set eko_ms90_plus[].host to the C-BOX IP address, usually 192.168.2.20, and eko_ms90_plus[].port to TCP port 502. The app no longer uses a USB-to-RS485 adapter or COM port for EKO.
If you prefer to use pip and venv instead of UV:
- Open a terminal in the
svcfolder
cd svc- Create and activate a virtual environment
2.1 Windows PowerShell
python -m venv .venv
.venv\Scripts\Activate.ps12.2 Windows CMD
.venv\Scripts\activate2.3 Mac Linux
python3 -m venv .venv
source .venv/bin/activate- Install packages
pip install --upgrade pip
pip install -r requirements.txt- Create your
.envfile from the example
4.1 Windows
copy .env.example .env4.2 Mac Linux
cp .env.example .env- Start the server
python main.pyYou should see Uvicorn running on port 8000
Open the API docs in a browser at http://127.0.0.1:8000/docs
- Open a new terminal in the
webfolder
cd web- Install packages
npm install- Start the dev server
npm run devOpen the link shown by Vite usually http://127.0.0.1:5173
You should see the control interface
cd web
npm run typecheck
npm test
npm run buildWindows PowerShell uses the same commands.
- The header shows service status
- Pick a group set a level press
Tint Group - Move a slider on any panel and press
Apply - Press
Refreshin the header to reload state
After the backend is running:
Invoke-RestMethod http://127.0.0.1:8000/sensors
Invoke-RestMethod http://127.0.0.1:8000/metrics/latestThen open the HMI and verify the sensor cards, live graphs, and Logs -> Sensor log.
For EKO on site:
- Open the C-BOX web UI from the site computer, usually
http://192.168.2.20/. - Confirm the live readings page updates.
- Open
Modbus -> Setup. - Confirm Modbus TCP access is enabled.
uv: command not found: install uv, then open a new terminal and runuv --version.- Wrong Python version: install Python 3.11, 3.12, or 3.13 and run
uv python pin 3.13fromsvcif needed. - Missing Python packages: run
cd svcthenuv sync. For pip/venv, rerunpip install -r requirements.txt. npm: command not found: install Node.js LTS and open a new terminal.- Frontend dependency issues: run
cd web, deletenode_modulesif needed, thennpm install. - Backend port already in use: stop the other process using port
8000, or runuv run uvicorn main:app --host 0.0.0.0 --port 8001. - C-BOX web UI unreachable: confirm the site computer is on the C-BOX network, verify the IP address, and check Ethernet cabling/firewall rules.
- EKO Modbus read failures: confirm C-BOX Modbus TCP is enabled and TCP
502is reachable. GET /sensorsis empty in real mode: checkSVC_MODE=real,SENSORS_CONFIG_FILE, and required sensor config fields. Real mode does not create simulated sensors as fallback.
The image build itself is mode-neutral. The app chooses simulator or real mode at runtime.
The normal compose run uses simulator mode by default. Use real mode only on the site computer after svc/data/window_mapping.json and svc/data/sensors_config.json have been checked for the real trailer hardware.
To build a new image:
podman build -t glazing-control-app .To run the single container in simulator mode:
On macOS / Linux:
podman run --rm -p 8000:8000 \
-v "$(pwd)/svc/data:/app/svc/data" \
-v "$(pwd)/svc/db-backups:/app/db-backups" \
-e SVC_DB_BACKUP_DIR=/app/db-backups \
-e SVC_DB_BACKUP_INTERVAL_HOURS=24 \
glazing-control-appOn Windows (PowerShell):
podman run --rm -p 8000:8000 `
-v "${PWD}\svc\data:/app/svc/data" `
-v "${PWD}\svc\db-backups:/app/db-backups" `
-e SVC_DB_BACKUP_DIR=/app/db-backups `
-e SVC_DB_BACKUP_INTERVAL_HOURS=24 `
glazing-control-appTo run the single container in real mode:
On macOS / Linux:
podman run --rm -p 8000:8000 \
-v "$(pwd)/svc/data:/app/svc/data" \
-v "$(pwd)/svc/db-backups:/app/db-backups" \
-e SVC_MODE=real \
-e SVC_DB_BACKUP_DIR=/app/db-backups \
-e SVC_DB_BACKUP_INTERVAL_HOURS=24 \
-e HALIO_API_URL= \
-e HALIO_SITE_ID= \
-e HALIO_API_KEY= \
glazing-control-appOn Windows (PowerShell):
podman run --rm -p 8000:8000 `
-v "${PWD}\svc\data:/app/svc/data" `
-v "${PWD}\svc\db-backups:/app/db-backups" `
-e SVC_MODE=real `
-e SVC_DB_BACKUP_DIR=/app/db-backups `
-e SVC_DB_BACKUP_INTERVAL_HOURS=24 `
-e HALIO_API_URL= `
-e HALIO_SITE_ID= `
-e HALIO_API_KEY= `
glazing-control-appThe compose configuration builds the same root image shown above. The backend serves the built frontend from the same container, so there is only one service to run and one port to open. Compose defaults to SVC_MODE=sim.
- Ensure the Podman system service/machine is running (via Podman Desktop or command line:
podman machine start). - Build the app image:
(Note:
podman compose build
podman composerequires a compose provider such as Docker Compose orpodman-compose. If Podman reports that no compose provider was found, install one of those providers or use the directpodman runcommand above.) - Start the app in detached mode:
podman compose up -d
- Once running, you can access:
- Frontend UI (HMI):
http://localhost:8000 - Backend API:
http://localhost:8000
- Frontend UI (HMI):
- To stop and remove the compose-managed container and network:
podman compose down
The compose service uses restart: unless-stopped. If the app process exits or the container crashes while Podman is running, Podman should restart it. If you run podman compose down, the container is removed and will not restart until you run podman compose up -d again.
The SQLite database is audit.db inside svc/data. Compose bind-mounts ./svc/data to /app/svc/data, so the database persists on the host across container rebuilds, container restarts, and image updates.
To write periodic SQLite backup copies to a Box-synced directory, set these before podman compose up -d:
On Windows (PowerShell):
$env:SVC_DB_BACKUP_INTERVAL_HOURS = "24"
$env:SVC_DB_BACKUP_DIR = "$env:USERPROFILE\Box\GlazingControlBackups"
podman compose up -dOn macOS / Linux:
export SVC_DB_BACKUP_INTERVAL_HOURS=24
export SVC_DB_BACKUP_DIR="$HOME/Box/GlazingControlBackups"
podman compose up -dThe app writes backup files named like audit-20260630-120000.db. Set SVC_DB_BACKUP_INTERVAL_HOURS=0 to disable backups. The first backup is written when the app starts, then again every configured interval.
restart: unless-stopped is the container restart policy. It restarts the app after a crash and after Podman is running again, but the machine also has to start Podman itself after boot.
On Linux, enable the Podman restart service:
sudo systemctl enable --now podman-restart.serviceFor Podman Desktop or Podman Machine on Windows/macOS, enable Podman Desktop to start at login, or configure a user login/startup task that runs:
podman machine start
podman compose -f C:\path\to\GlazingControlApp\docker-compose.yml up -dDo not use podman compose down for routine restarts on the site computer; it removes the container. Use podman compose stop to stop it temporarily, then podman compose up -d to bring it back.
Use this only on the site computer with the real Halio API values and checked sensor config.
On macOS / Linux:
export SVC_MODE=real
export HALIO_API_URL=http://192.168.2.200:8084/api
export HALIO_SITE_ID=<site-id>
export HALIO_API_KEY=<api-key>
podman compose up -dOn Windows (PowerShell):
$env:SVC_MODE = "real"
$env:HALIO_API_URL = "http://192.168.2.200:8084/api"
$env:HALIO_SITE_ID = "<site-id>"
$env:HALIO_API_KEY = "<api-key>"
podman compose up -dAlternatively, if you have an .env file:
podman compose --env-file .\svc\.env up -dVerify the mode after startup:
Invoke-RestMethod http://127.0.0.1:8000/healthIf podman compose build fails on Windows with an access error for a generated cache directory such as .pytest_cache, fix the directory ACLs from an elevated PowerShell terminal:
takeown /F .pytest_cache /R /D Y
icacls .pytest_cache /grant "$env:USERNAME:(OI)(CI)F" /T
Remove-Item -Recurse -Force .pytest_cacheThen rerun:
podman compose build
podman compose up -d