Files
jackos1998 aafd487b83 Add Home Assistant integration
Token-authenticated custom component exposing live per-club member counts
as sensors under a single "West Wood Club" device, fed by one coordinator
polling `/v1/Clubs/WhoIsInCount`.

Packaged via `buildHomeAssistantComponent` with a flake package + overlay so
it can be used in `services.home-assistant.customComponents`.

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

85 lines
2.4 KiB
Python
Executable File

#!/usr/bin/env python
"""Log in to the West Wood Club (PerfectGym) API and print the bearer token.
The token is written to stdout (and nothing else), so it can be piped to a file:
python login.py > token.txt
Credentials are read from the WESTWOOD_EMAIL and WESTWOOD_PASSWORD environment
variables; any that are missing are prompted for on the terminal. Prompts and
errors go to stderr, keeping stdout clean for the token.
"""
import getpass
import json
import os
import sys
import urllib.error
import urllib.request
BASE_URL = 'https://goapi2.perfectgym.com'
WHITE_LABEL_ID = '7d073db5-0ef8-4d78-89ec-4a8bebaf4cbc'
USER_AGENT = (
'West Wood Club/1.28.3.0 '
'(com.perfectgym.perfectgymgo2.westwoodclub; build:1028003000; Android 16)'
)
def log_in(email: str, password: str) -> str:
"""Return the bearer token (the value for the Authorization header)."""
body = json.dumps(
{
'email': email,
'password': password,
'clientApplicationInfo': {
'type': 'whitelabel',
'whiteLabelId': WHITE_LABEL_ID,
},
}
).encode()
request = urllib.request.Request(
f'{BASE_URL}/v1/Authorize/LogInWithEmail',
data=body,
method='POST',
headers={
'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8',
'Accept-Language': 'en',
'X-Go-App-Platform': 'Android',
'X-Go-App-Version': '1.28.3',
'X-Go-White-Label-ID': WHITE_LABEL_ID,
'User-Agent': USER_AGENT,
},
)
with urllib.request.urlopen(request) as response:
payload = json.load(response)
if payload.get('errors'):
raise SystemExit(f'login failed: {payload["errors"]}')
data = payload.get('data') or {}
token = data.get('token')
if not token:
raise SystemExit(f'no token in response: {payload}')
return token
def main() -> None:
email = os.environ.get('WESTWOOD_EMAIL') or input('Email: ')
password = os.environ.get('WESTWOOD_PASSWORD') or getpass.getpass('Password: ')
try:
token = log_in(email, password)
except urllib.error.HTTPError as exc:
raise SystemExit(f'HTTP {exc.code}: {exc.read().decode("utf-8", "replace")}')
except urllib.error.URLError as exc:
raise SystemExit(f'request failed: {exc.reason}')
print(token)
if __name__ == '__main__':
main()