Created
March 4, 2026 12:43
-
-
Save AndrewAubury/7ea1cf8470d0a5f230ca910538b32f2a to your computer and use it in GitHub Desktop.
PureGym Attenance to InfluxDB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const GYMS = { | |
| "dunstable": 111, | |
| "luton": 462, | |
| "haywards_heath": 359, | |
| }; | |
| /** | |
| * Authenticate and return Bearer token | |
| */ | |
| async function authenticate(email, pin) { | |
| const body = new URLSearchParams({ | |
| grant_type: "password", | |
| username: email, | |
| password: pin, | |
| scope: "pgcapi", | |
| client_id: "ro.client", | |
| }); | |
| const res = await fetch("https://auth.puregym.com/connect/token", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/x-www-form-urlencoded", | |
| "User-Agent": "PureGym/1523 CFNetwork/1312 Darwin/21.0.0", | |
| }, | |
| body, | |
| }); | |
| if (!res.ok) { | |
| throw new Error(`Auth failed: ${res.status}`); | |
| } | |
| const json = await res.json(); | |
| return json.access_token; | |
| } | |
| async function fetchGyms(token) { | |
| const res = await fetch( | |
| `https://capi.puregym.com/api/v2/gyms/`, | |
| { | |
| headers: { | |
| "Authorization": `Bearer ${token}`, | |
| "User-Agent": "PureGym/1523 CFNetwork/1312 Darwin/21.0.0", | |
| }, | |
| } | |
| ); | |
| if (!res.ok) { | |
| const err = res.statusText | |
| return {"err": err}; | |
| //throw new Error(`Attendance failed (${gymId}): ${res.status}`); | |
| } | |
| const json = await res.json(); | |
| return json | |
| } | |
| /** | |
| * Fetch attendance for one gym | |
| */ | |
| async function fetchAttendance(token, gymId) { | |
| const res = await fetch( | |
| `https://capi.puregym.com/api/v2/gymSessions/gym?gymId=${gymId}`, | |
| { | |
| headers: { | |
| "Authorization": `Bearer ${token}`, | |
| "User-Agent": "PureGym/1523 CFNetwork/1312 Darwin/21.0.0", | |
| }, | |
| } | |
| ); | |
| if (!res.ok) { | |
| return 0; | |
| //throw new Error(`Attendance failed (${gymId}): ${res.status}`); | |
| } | |
| const json = await res.json(); | |
| if(json.TotalPeopleInGym == null){ | |
| return 0; | |
| }else{ | |
| return json.TotalPeopleInGym; | |
| } | |
| } | |
| export default { | |
| async scheduled(event, env, ctx) { | |
| console.log("Scheduled run at", event.scheduledTime) | |
| ctx.waitUntil(run()) | |
| }, | |
| async fetch(request, env, ctx) { | |
| const result = await run({ returnDetails: true }) | |
| return new Response(JSON.stringify(result, null, 2), { | |
| status: result.ok ? 200 : 500, | |
| headers: { | |
| "Content-Type": "application/json" | |
| } | |
| }) | |
| } | |
| } | |
| async function run(options = {}) { | |
| const written = [] | |
| let influxResponse = null | |
| const token = await authenticate( | |
| 'EMAIL_HERE', | |
| 'PIN_HERE', | |
| ); | |
| const result = {}; | |
| await Promise.all( | |
| Object.entries(GYMS).map(async ([name, gymId]) => { | |
| try { | |
| result[name] = await fetchAttendance(token, gymId); | |
| } catch { | |
| result[name] = null; | |
| } | |
| }) | |
| ); | |
| const data = result; | |
| // Build InfluxDB line protocol | |
| const lines = [] | |
| for (const [location, count] of Object.entries(data)) { | |
| lines.push(`gym_occupancy,location=${location} count=${count}`) | |
| written.push({ location, count }) | |
| } | |
| const body = lines.join("\n") | |
| // Write to InfluxDB | |
| const influxRes = await fetch( | |
| "https://influxdb.example.com/api/v2/write?org=org_here&bucket=bucket_here&precision=s", | |
| { | |
| method: "POST", | |
| headers: { | |
| "Authorization": "Token token_here", | |
| "Content-Type": "text/plain" | |
| }, | |
| body | |
| } | |
| ) | |
| const influxText = await influxRes.text() | |
| if (!influxRes.ok) { | |
| console.error("Influx write failed", influxRes.status, influxText) | |
| return options.returnDetails | |
| ? { | |
| ok: false, | |
| written, | |
| influx: { | |
| status: influxRes.status, | |
| body: influxText | |
| }, | |
| } | |
| : undefined | |
| } | |
| influxResponse = { | |
| status: influxRes.status, | |
| body: influxText || "OK" | |
| } | |
| return options.returnDetails | |
| ? { | |
| ok: true, | |
| written, | |
| influx: influxResponse, | |
| } | |
| : undefined | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment