Files
hass-west-wood/AGENTS.md
T
jackos1998 07804e8ae8 Expand AGENTS.md: checks, brand assets, decompiled-APK note
Add a Checks section (`py_compile` + `nix build`), note the `brand/` assets
and their source, broaden the backtick-quoting rule, refine the token-expiry
guidance, and explain using decompiled APK output if provided.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:42:32 +01:00

6.3 KiB

AGENTS.md

Purpose

Groundwork for a Home Assistant integration for West Wood Club (an Irish gym chain). The West Wood app is a white-label build of PerfectGym Go, so the integration targets PerfectGym's hosted backend at https://goapi2.perfectgym.com.

The repo holds the reverse-engineering capture, the API docs derived from it, a token helper, and the Home Assistant custom component itself (see Integration).

Layout

  • api.md — documented PerfectGym endpoints (login, club list, live occupancy), secrets redacted. The source of truth for API behaviour; extend it as more endpoints are mapped.
  • get-token.py — logs in and prints a bearer token to stdout (stdlib only).
  • custom_components/west_wood_club/ — the Home Assistant integration. Its brand/ dir holds the integration icon/logo, extracted from the capture's westwoodclub-ie.png asset.
  • android-flows.mitm — mitmproxy capture of the app's traffic. Gitignored and untracked: it contains real credentials and a bearer token in cleartext. Never commit it or copy its secrets into tracked files.
  • token.txt — a captured/extracted bearer token. Also gitignored.
  • flake.nix / .envrc — Nix dev shell (provides mitmproxy).

Dev environment

Nix flake + nix-direnv. direnv allow enters a shell with mitmproxy/python.

To run Python/mitmproxy from a non-interactive shell (where direnv does not auto-activate and there's no python on PATH), use direnv exec .:

direnv exec . python script.py

This reuses nix-direnv's cached dev shell (~0.06s). Prefer it over nix develop --command ..., which re-evaluates the flake every call (slow, and noisy with "Git tree is dirty" warnings).

Gotchas:

  • Nix flakes only see git-tracked files. git add new files before nix develop / nix flake lock, or Nix errors with "not tracked by Git".
  • Use absolute paths when a script opens android-flows.mitmnix develop --command resets cwd to the repo root (direnv exec . keeps the current dir).

Code style

Python uses single-quoted strings ('...'). Reformat with ruff format --config "format.quote-style='single'" <paths> (ruff is available via nix run nixpkgs#ruff). Docstrings stay triple-double-quoted (""").

In prose (commit messages, docs, comments), backtick-quote anything code-like — paths, filenames, identifiers, commands, endpoints, HTTP headers, field/JSON keys, UUIDs/IDs, env vars — rather than plain or double-quoted text. If in doubt and it's a literal token from code or an API, backtick it.

Working with the capture

Read flows with the mitmproxy Python API:

from mitmproxy.io import FlowReader
from mitmproxy.http import HTTPFlow

with open('/abs/path/android-flows.mitm', 'rb') as f:
    for flow in FlowReader(f).stream():
        if isinstance(flow, HTTPFlow) and 'perfectgym.com' in flow.request.host:
            ...  # flow.request / flow.response

When dumping flows, redact Authorization / Cookie headers and the login body (email + password) before writing anything to a tracked file.

Some questions the capture can't answer (token lifecycle, the white-label ID, error codes) need the app itself. If you're pointed at decompiled APK output (e.g. apktool smali/), grep it there — but it's R8-obfuscated: class names are mangled and library types (e.g. OkHttp) may be shrunk/repackaged, so a missing grep hit is not proof of absence. No such decompilation lives in this repo.

API essentials

Full detail in api.md. Quick reference:

  • Responses are wrapped { "data": ..., "errors": ... }; errors is null on success.
  • Auth: POST /v1/Authorize/LogInWithEmail (white-label ID goes in the body) → reuse the returned bearer <token> as the Authorization header. The token most likely doesn't expire (no refresh token in the response, app appears to store no credentials); on 401/403 get a fresh one. See api.md.
  • Authenticated endpoints need only the Authorization header — the X-Go-* headers and app User-Agent the app sends are not required (verified against the clubs endpoint).
  • Live occupancy: GET /v1/Clubs/WhoIsInCountcount per clubId (the main sensor signal). clubId maps to id from GET /v1/Clubs/Clubs.

Integration

custom_components/west_wood_club/ is a UI-configured (config-flow) integration.

  • Auth model: the user pastes a long-lived bearer token (from get-token.py); no credentials are stored. A rejected token (WestWoodAuthError → coordinator raises ConfigEntryAuthFailed) triggers HA's reauth flow to paste a fresh one.
  • One device, N sensors: a single DataUpdateCoordinator polls WhoIsInCount once per interval for all clubs; one SensorEntity per selected club reads its club_id out of coordinator.data. All sensors share one device (identifiers={(DOMAIN, entry.entry_id)}) named "West Wood Club".
  • Config flow steps: user and reauth are HA-fixed names (dispatched by the flow source); clubs and reauth_confirm are reached because a form was shown with that step_id (HA calls async_step_<step_id> on submit). Every step name must also have a matching key under config.step in strings.json.
  • Nix: flake.nix exposes packages.west_wood_club (built with buildHomeAssistantComponent) and overlays.default, which adds it to pkgs.home-assistant-custom-components. On a NixOS host, apply the overlay and list it in services.home-assistant.customComponents; it must be built against the same Python as the host's home-assistant. Bump version in both manifest.json and the flake on code changes. The integration has no external requirements, so no extra Nix packaging is needed.

Checks

There is no test suite. Verify changes with:

  • direnv exec . python -m py_compile custom_components/west_wood_club/*.py — fast syntax check of the component.
  • nix build .#west_wood_club — builds the component and runs nixpkgs' manifest and import checks (the closest thing to CI here). Remember to git add new files first, or the flake won't see them.

Security

The capture and token.txt hold live credentials/tokens. Keep them gitignored, and prefer the WESTWOOD_EMAIL / WESTWOOD_PASSWORD env vars (read by get-token.py) over hardcoding credentials anywhere.