# Park Score Reporting — Integration Guide

**SisyFox GmbH** · Customer Documentation  
Version 1.2.1 · June 2026

---

## Table of Contents

1. [Overview](#1-overview)
2. [Integration Modes](#2-integration-modes)
3. [JSON Payload](#3-json-payload)
4. [Data Behavior & State Machine](#4-data-behavior--state-machine)
5. [Network & Connectivity](#5-network--connectivity)
6. [Device Configuration](#6-device-configuration)
7. [Integration Simulator](#7-integration-simulator)
8. [Setup Checklist](#8-setup-checklist)

---

## 1. Overview

The SisyFox Park Score Reporting system allows your park management software (e.g. Jumpilot) to receive live game scores from our attraction devices in real time.

We support two integration modes that can be used independently or in combination:

| Mode | Description |
|---|---|
| **Post To Park** | Our device actively pushes score data to your server via HTTP POST |
| **Expose Endpoint** | Our device exposes an HTTP endpoint that your server polls on a schedule |

The system is game-agnostic and works across all SisyFox attractions (Jumplings, FoxBall, etc.).

---

## 2. Integration Modes

### Mode 1 — Post To Park

Our device sends score data to an endpoint on your server. You provide us with the URL and credentials, and we configure the device accordingly.

```
SisyFox Device  ──POST──▶  https://your-server.com/api/attractions/points
```

**Details:**

- Authentication via HTTP Basic Auth (you provide the credentials)
- Payload format: JSON Array (see [Section 3](#3-json-payload))
- Trigger: either on every status change, or at a fixed time interval (your choice)

**Post Trigger Options:**

| Option | Behavior | Recommended for |
|---|---|---|
| `OnStatusChange` | One POST is sent each time the game status changes (`idle → playing → completed`) | Production |
| `Interval` | A POST is sent at a fixed interval regardless of status changes | Debugging / heartbeat |

---

### Mode 2 — Expose Endpoint

Our device runs a lightweight HTTP server that your system can poll at any interval you choose.

```
Your Server  ──GET──▶  http://<device-ip>:8080/points
```

**Details:**

- No authentication required by default
- Payload format: JSON Array (see [Section 3](#3-json-payload))
- Port is configurable (default: `8080`)
- You control the polling interval

---

### Mode 3 — Both

Both modes run simultaneously. This is useful during initial integration and testing, or if you prefer redundancy in production.

---

## 3. JSON Payload

Both integration modes use the same JSON format — an array containing one entry per active player.

### Example

```json
[
  {
    "deviceID": "jumplings-1",
    "gameID": "Ninja Jump",
    "roundID": "20260506_103319_734639760",
    "playerID": 0,
    "points": 12.50,
    "rating": 1.00,
    "status": "completed",
    "timestamp": "2026-05-06T08:34:18.963Z"
  },
  {
    "deviceID": "jumplings-1",
    "gameID": "Ninja Jump",
    "roundID": "20260506_103319_734639760",
    "playerID": 1,
    "points": 8.00,
    "rating": 0.75,
    "status": "completed",
    "timestamp": "2026-05-06T08:34:18.963Z"
  }
]
```

### Field Reference

| Field | Type | Description |
|---|---|---|
| `deviceID` | String | Unique identifier of the attraction device |
| `gameID` | String | Name of the current game or mini-game |
| `roundID` | String | Unique ID of the current round — format: `YYYYMMDD_HHMMSS_<random>` |
| `playerID` | Int | Player slot index, zero-based |
| `points` | Float | Player score for the current round (see behavior below) |
| `rating` | Float | Player rating for the current round (see behavior below) |
| `status` | String | Current game status: `idle`, `playing`, or `completed` |
| `timestamp` | String | ISO-8601 UTC timestamp of when the data was generated |

---

## 4. Data Behavior & State Machine

### Game Status Values

| Status | Meaning |
|---|---|
| `idle` | No active session — the attraction is in attract mode or between rounds |
| `playing` | A round is currently in progress |
| `completed` | The round has ended and final scores are available |

### Points & Rating During a Round

The `points` and `rating` fields use `-1.00` as a sentinel value to signal that no valid data is available yet. Your server should treat any negative value as "not yet final" and not persist it.

**Points:**

| Status | Value | Meaning |
|---|---|---|
| `idle` | `-1.00` | No active round |
| `playing` | `-1.00` | Round in progress, scores not final |
| `completed` | Actual value, e.g. `12.50` | Final score for a real player |
| `completed` | `-1.00` | Player slot was not occupied by a real player (e.g. bot) |

**Rating:**

| Status | Value | Meaning |
|---|---|---|
| `idle` | `-1.00` | No active round |
| `playing` | `-1.00` | Round in progress, rating not final |
| `completed` | `0.00` – `1.00` | Final rating for a real player (0 = poor, 1 = excellent) |
| `completed` | `-1.00` | Player slot was not occupied by a real player |

**How rating is calculated:**

- **Jumplings:** Based on the player's final placement in the round
- **FoxBall:** Based on the number of stars the player received

### State Flow

A typical round follows this sequence:

```
       idle  ──────────────────────────────────────┐
   (points/rating: -1)                             │
         │                                         │
         │  game starts                            │
         ▼                                         │
      playing                                      │
   (points/rating: -1)                             │
         │                                         │
         │  round ends                             │
         ▼                                         │
     completed                                     │
   (points/rating: actual)                         │
         │                                         │
         │  attract mode / next round              │
         └─────────────────────────────────────────┘
```

### Aborted Rounds

If a round is cancelled mid-game (e.g. guests leave the attraction), the status transitions directly from `playing` back to `idle` — **without passing through `completed`**. Your server will not receive a `completed` status for that round and should handle this gracefully.

---

## 5. Network & Connectivity

### Reaching Our Device (Mode 2)

Our device exposes the endpoint on a fixed port. You reach it via the device's local IP address:

```
http://<device-ip>:8080/points
```

For stable operation we recommend assigning a static IP or a DHCP reservation for the device on your network.

### Reaching Your Server (Mode 1)

Your server endpoint needs to be reachable from our device over the local network or internet. We support both local hostnames and public URLs:

| Option | Example |
|---|---|
| mDNS / local hostname | `http://park-server.local:8080/api/attractions/points` |
| Direct IP | `http://192.168.1.10:8080/api/attractions/points` |
| Public URL | `https://your-park-system.com/api/attractions/points` |

---

## 6. Device Configuration

All settings are stored on the device at `/var/sisyfox/park_config.json` and can be adjusted through the in-game settings menu.

### Opening the Settings Menu

#### Jumplings:
1. Connect a keyboard to the device.
2. Press `Esc` to open the device menu.
3. Navigate to **Score Reporting**.

#### FoxBall:
1. Connect via the Remote (see [Remote Setup Manual](https://sisyfox.com/FoxBall_user-interface.pdf)).
2. Open **Device Settings** → **Score Reporting**.

### Configuration Reference

| Setting | Type | Description |
|---|---|---|
| `enableReporting` | Bool | Master switch — enables or disables the entire reporting system |
| `deviceID` | String | Unique identifier for this device (e.g. `jumplings-1`) |
| `reportingMode` | Enum | `PostToPark`, `ExposeEndpoint`, or `Both` |
| `parkServerUrl` | String | Your server's endpoint URL (Mode 1 only) |
| `attractionID` | String | Username for HTTP Basic Auth (Mode 1 only) |
| `password` | String | Password for HTTP Basic Auth (Mode 1 only) |
| `postMode` | Enum | `OnStatusChange` or `Interval` (Mode 1 only) |
| `postIntervalSeconds` | Float | POST interval in seconds — only used when `postMode` is `Interval` |
| `localPort` | Int | Port for our local HTTP server (Mode 2 only, default: `8080`) |

---

## 7. Integration Simulator

To allow you to begin development and testing **before receiving a physical device**, we provide an online integration simulator that replicates the behavior of a real SisyFox attraction.

**Simulator URL:** https://simulate.sisyfox.com

### What the Simulator Does

The simulator exposes the same HTTP interface as a real device and generates realistic payloads with configurable players, scores, and game states. You can use it to:

- Test your server's handling of incoming POST requests (Mode 1)
- Test your polling logic against a live `/points` endpoint (Mode 2)
- Simulate a complete game round from `idle` through `playing` to `completed`
- Simulate an **aborted round** where `completed` is never reached
- Verify that your system correctly handles the `-1.00` sentinel values during `idle` and `playing`

### Simulator Interface

The simulator UI is divided into three areas:

**Players** — Set the number of player slots, and randomize scores and ratings. Each player can be toggled between a real player (receives actual scores on `completed`) and a bot (always returns `-1.00`). The player count and score ranges adjust automatically based on the selected device type.

**General** — Select the device type (Jumplings or FoxBall), configure the device ID, game, and the URL of your server endpoint. The game list updates based on the selected device type. If your endpoint requires Basic Auth, enter the Attraction ID and Password here.

**Round Simulation** — Run a full automated round or an aborted round with configurable timing.

### Running a Simulation

1. Select the **Device Type** you want to simulate — Jumplings or FoxBall (see below for differences)
2. Enter your server's endpoint URL under **POST URL** in the General panel
3. Set your credentials if your endpoint requires Basic Auth
4. Adjust the player configuration as needed, or click **Randomize** to generate realistic scores
5. In the **Round Simulation** panel, configure the timing:
   - **Idle → Playing delay** — how long the device stays in `idle` before the round starts
   - **Playing duration** — how long the round lasts
   - **Completed → Idle delay** — how long the `completed` state is held before returning to `idle`
6. Choose **Post Mode**: `OnStatusChange` to POST only on status transitions, or `Interval` to POST repeatedly throughout the round
7. Click **▶ Full Round** to run a complete round, or **✕ Aborted Round** to simulate a cancelled game

The timeline indicator and progress bar reflect the current phase in real time.

### Device Types

#### FoxBall

FoxBall is a single-player attraction. There are no bot players — each round always has exactly one real player.

- **Player count:** always 1
- **Score range:** typically 0 – 1,500 points. Note that certain game modes (e.g. the Downhill endless/arcade variants) can produce scores above this range — use the `rating` field as a normalised indicator of performance when exact score ranges matter.
- **Rating:** linearly interpolated between the estimated minimum and maximum score for the round. A player who scores near the top of the range receives a rating close to `1.00`; a low score yields a rating close to `0.00`.

#### Jumplings

Jumplings supports 3 to 5 players per round. Player slots that are not occupied by a real guest are filled by bots.

- **Player count:** 3 – 5
- **Score range:** approximately 0.00 – 5.00 points (the scale is intentionally compact)
- **Bots:** bot scores are never reported. If a player slot was occupied by a bot, `points` and `rating` are both returned as `-1.00` regardless of what the bot achieved in-game.
- **Rating:** based on final placement. First place receives `1.00`, last place receives `0.00`, and intermediate placements are interpolated linearly based on the total number of real players in the round. For example, in a 4-player round: 1st = `1.00`, 2nd = `0.67`, 3rd = `0.33`, 4th = `0.00`.


### Expose Endpoint

The simulator also exposes a live `/points` endpoint identical to Mode 2:

```
GET https://simulate.sisyfox.com/points
```

You can poll this endpoint directly from your server while a simulation is running to test your polling logic end-to-end.

### Running the Simulator Locally

For testing **Mode 1 (Post To Park)**, we recommend running the simulator locally on your own network. This eliminates public internet latency and means your park server can receive POSTs from the simulator just as it would from a real device — no firewall rules or public endpoints required.

**Download:** [sisyfox-score-simulator.zip](https://simulate.sisyfox.com/download/sisyfox-score-simulator.zip)

The ZIP contains everything you need. Setup requires Python 3.10 or newer — available at https://www.python.org/downloads/.

```bash
# 1. Unzip and open a terminal in the folder
# 2. Install dependencies
pip install -r requirements.txt

# 3. Start the simulator
python app.py
```

The simulator UI is then available at `http://localhost:5000` and the expose endpoint at `http://localhost:5000/points`.

If your park server needs to reach the simulator's `/points` endpoint from another machine on the same network, use your machine's local IP address instead of `localhost`:

```
http://<your-local-ip>:5000/points
```

> The hosted version at `https://simulate.sisyfox.com` and the local version are functionally identical. Use the hosted version for a quick start or when your server has a public endpoint; use the local version when testing Mode 1 on a closed network.

### Known Differences from a Real Device

The simulator is designed to closely replicate real device behavior, but there are a few minor differences to be aware of:

- **Double `idle` on `OnStatusChange`** — When a full round completes and transitions back to `idle`, the `OnStatusChange` mode will trigger two consecutive POSTs with `status: idle` in quick succession (one at round start, one at round end). A real device handles this internally and will only send one. Your server should be able to handle duplicate `idle` payloads gracefully.
- **Network latency** — The hosted simulator runs on a cloud server. POSTs travel over the public internet, while a real device communicates over your local network. Use the local simulator to avoid this.
- **No physical attract mode** — On a real device, `idle` corresponds to the attract video playing on screen. The simulator simply holds the `idle` state for the configured delay.

---

## 8. Setup Checklist

### For Post To Park (Mode 1)

**You provide us with:**

- ▢ Your endpoint URL
- ▢ The `attractionID` (username) and `password` you want us to use

**We configure on the device:**

- ▢ `reportingMode` → `PostToPark`
- ▢ `parkServerUrl` → your endpoint
- ▢ `postMode` → `OnStatusChange` (recommended) or `Interval`

**You verify:**

- ▢ Your server receives a POST with `status: idle` when the attraction is idle
- ▢ Your server receives a POST with `status: playing` and `points: -1.00` when a round starts
- ▢ Your server receives a POST with `status: completed` and actual scores when a round ends
- ▢ Your server handles aborted rounds (no `completed` received after `playing`)

---

### For Expose Endpoint (Mode 2)

**You provide us with:**

- ▢ Your preferred polling interval
- ▢ Whether you need authentication on our endpoint

**We configure on the device:**

- ▢ `reportingMode` → `ExposeEndpoint`
- ▢ `localPort` (default: `8080`)

**You verify:**

- ▢ Your server can reach the device at `http://<device-ip>:8080/points`
- ▢ Polling returns `status: idle` with `points: -1.00` between rounds
- ▢ Polling returns `status: playing` during a round
- ▢ Polling returns `status: completed` with actual scores after a round ends

---

*For integration support, contact SisyFox GmbH.*