Add OpenAPI v2 spec; remove draft docs
Track the portal v2 API as openapi.yaml and drop the superseded portal_api_v2_schema.md and user_manual_draft.md.
This commit is contained in:
+467
@@ -0,0 +1,467 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Radiant Wave Portal API (v2)
|
||||
version: 2.0.0
|
||||
description: |
|
||||
Read API used by the Radiant Wave kiosk plus the automation onboarding
|
||||
webhook.
|
||||
|
||||
## Authentication
|
||||
The v2 API is **not** API-key gated. The former bearer-token middleware in
|
||||
`src/hooks.server.ts` is present but disabled, so v2 routes are served
|
||||
without a global API key.
|
||||
|
||||
Two endpoints carry their own credential semantics:
|
||||
|
||||
- `GET /api/v2/proclamations` optionally accepts the paired
|
||||
`x-license-key` / `x-email` headers to scope results to an entitled user.
|
||||
Supplying neither returns the full catalogue; supplying only one, or a
|
||||
pair that does not match, is rejected.
|
||||
- `POST /api/v2/automations/create/account` is protected by an HMAC-SHA256
|
||||
request signature (see the operation description).
|
||||
servers:
|
||||
- url: https://portal.radiantwavetech.com
|
||||
description: Production
|
||||
- url: http://localhost:5173
|
||||
description: Local development
|
||||
tags:
|
||||
- name: Catalogue
|
||||
description: Reference data for categories, groups, frequencies and palettes.
|
||||
- name: Proclamations
|
||||
description: Proclamation content, optionally scoped by license entitlement.
|
||||
- name: License
|
||||
description: License key validation.
|
||||
- name: Automations
|
||||
description: Signed machine-to-machine webhooks.
|
||||
paths:
|
||||
/api/v2/categories:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List proclamation categories
|
||||
operationId: listCategories
|
||||
responses:
|
||||
'200':
|
||||
description: All proclamation categories.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Category'
|
||||
/api/v2/proclamation-categories:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List proclamation categories (dedicated alias)
|
||||
description: Alias of `/api/v2/categories`; returns the identical payload.
|
||||
operationId: listProclamationCategories
|
||||
responses:
|
||||
'200':
|
||||
description: All proclamation categories.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Category'
|
||||
/api/v2/groups:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List proclamation groups
|
||||
description: Groups are ordered by `sortOrder` ascending.
|
||||
operationId: listGroups
|
||||
responses:
|
||||
'200':
|
||||
description: All proclamation groups.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Group'
|
||||
/api/v2/proclamation-groups:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List proclamation groups (dedicated alias)
|
||||
description: Alias of `/api/v2/groups`; returns the identical payload.
|
||||
operationId: listProclamationGroups
|
||||
responses:
|
||||
'200':
|
||||
description: All proclamation groups.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Group'
|
||||
/api/v2/frequencies:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List canonical solfeggio frequencies
|
||||
description: |
|
||||
Returns the nine canonical frequencies (ids 1-9) the kiosk expects.
|
||||
These are static values, not the portal database autoincrement ids.
|
||||
operationId: listFrequencies
|
||||
responses:
|
||||
'200':
|
||||
description: The nine canonical frequencies.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
minItems: 9
|
||||
maxItems: 9
|
||||
items:
|
||||
$ref: '#/components/schemas/Frequency'
|
||||
/api/v2/color-palettes:
|
||||
get:
|
||||
tags: [Catalogue]
|
||||
summary: List color palettes per canonical frequency
|
||||
description: |
|
||||
Returns one 7-color palette for each canonical frequency id (1-9).
|
||||
Palette rows stored in the database take precedence; ids without a row
|
||||
fall back to the built-in canonical palette.
|
||||
operationId: listColorPalettes
|
||||
responses:
|
||||
'200':
|
||||
description: Nine palettes, one per canonical frequency id.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
minItems: 9
|
||||
maxItems: 9
|
||||
items:
|
||||
$ref: '#/components/schemas/ColorPalette'
|
||||
/api/v2/proclamations:
|
||||
get:
|
||||
tags: [Proclamations]
|
||||
summary: List proclamations
|
||||
description: |
|
||||
Without entitlement headers, returns the full proclamation catalogue.
|
||||
|
||||
With both `x-license-key` and `x-email`, returns only the proclamations
|
||||
assigned to the matching user. Both headers must be present together and
|
||||
the key must belong to the user.
|
||||
|
||||
Proclamations whose frequency has no canonical id (for example 936 Hz)
|
||||
are omitted from the response.
|
||||
operationId: listProclamations
|
||||
parameters:
|
||||
- in: header
|
||||
name: x-license-key
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
description: License key. Must be supplied together with `x-email`.
|
||||
- in: header
|
||||
name: x-email
|
||||
schema:
|
||||
type: string
|
||||
format: email
|
||||
required: false
|
||||
description: Account email. Must be supplied together with `x-license-key`.
|
||||
responses:
|
||||
'200':
|
||||
description: Proclamation records with canonical frequency ids and embedded palettes.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Proclamation'
|
||||
'400':
|
||||
description: |
|
||||
Missing entitlement or the supplied key/email pair is invalid.
|
||||
Message is `not-authorized`, or
|
||||
`this key doesn't appear to belong to you` when the key does not
|
||||
belong to the specified user.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
/api/v2/license:
|
||||
post:
|
||||
tags: [License]
|
||||
summary: Validate a license key
|
||||
description: |
|
||||
Validates that the license key exists, is active, and belongs to the
|
||||
account matching `email_address`. A successful validation records
|
||||
`lastValidatedAt` on the key.
|
||||
operationId: validateLicense
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LicenseRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: The validated key and its owner email.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LicenseResponse'
|
||||
'400':
|
||||
description: |
|
||||
Validation failure or malformed body. Possible messages:
|
||||
`content-type must be application/json`, `invalid JSON body`,
|
||||
`request body is required`, `license_key is required`,
|
||||
`email_address is required`, `license key not found`,
|
||||
`license key is not active`, `user does not exist`,
|
||||
`key does not belong to specified user`.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
'413':
|
||||
description: The request body exceeded the 64 KiB limit.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
'429':
|
||||
description: Rate limit exceeded (10 requests per minute per IP).
|
||||
headers:
|
||||
Retry-After:
|
||||
schema:
|
||||
type: integer
|
||||
description: Seconds to wait before retrying.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
/api/v2/automations/create/account:
|
||||
post:
|
||||
tags: [Automations]
|
||||
summary: Create an account and license key (signed webhook)
|
||||
description: |
|
||||
Machine-to-machine onboarding webhook. The request must be signed with
|
||||
HMAC-SHA256 using `API_V2_ONBOARDING_SECRET`.
|
||||
|
||||
The canonical string that is signed is the newline-joined concatenation
|
||||
of:
|
||||
|
||||
```
|
||||
POST
|
||||
<path>
|
||||
<x-timestamp>
|
||||
<x-nonce>
|
||||
<raw request body>
|
||||
```
|
||||
|
||||
where `<path>` is `/api/v2/automations/create/account`. The signature is
|
||||
the lowercase hex digest.
|
||||
|
||||
On success, creates the user if they do not already exist, generates a
|
||||
license key, emails it, and returns the user id. Invalid or missing
|
||||
signatures return `401 { "error": "invalid signature" }` (the same
|
||||
response is used for every signature failure, including replay and
|
||||
timestamp skew).
|
||||
operationId: createAutomationAccount
|
||||
parameters:
|
||||
- in: header
|
||||
name: x-timestamp
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Unix epoch seconds. Must be within 300 seconds of the server clock.
|
||||
- in: header
|
||||
name: x-nonce
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Unique value per request. Replays within the skew window are rejected.
|
||||
- in: header
|
||||
name: x-signature
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[0-9a-f]{64}$'
|
||||
description: Lowercase hex HMAC-SHA256 of the canonical request string.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/OnboardingRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Account and license key created.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/OnboardingResponse'
|
||||
'400':
|
||||
description: |
|
||||
Malformed or invalid request. Possible messages:
|
||||
`request body is required`, `invalid JSON body`, or
|
||||
`email is required`.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
'401':
|
||||
description: Signature missing, malformed, expired, replayed, or incorrect.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
example: invalid signature
|
||||
'413':
|
||||
description: The request body exceeded the 64 KiB limit.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
'429':
|
||||
description: |
|
||||
Rate limit exceeded. Limits are 5 requests per minute per IP and
|
||||
3 requests per 24 hours per email address.
|
||||
headers:
|
||||
Retry-After:
|
||||
schema:
|
||||
type: integer
|
||||
description: Seconds to wait before retrying.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
'500':
|
||||
description: The account could not be created.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorMessage'
|
||||
components:
|
||||
schemas:
|
||||
Category:
|
||||
type: object
|
||||
required: [id, name, sortOrder]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
sortOrder:
|
||||
type: integer
|
||||
Group:
|
||||
type: object
|
||||
required: [id, name, sortOrder]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
sortOrder:
|
||||
type: integer
|
||||
Frequency:
|
||||
type: object
|
||||
required: [id, name, value]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 9
|
||||
description: Canonical id used by the kiosk.
|
||||
name:
|
||||
type: string
|
||||
value:
|
||||
type: integer
|
||||
description: Frequency in Hz.
|
||||
ColorPalette:
|
||||
type: object
|
||||
required: [solfeggioFrequencyId, colors]
|
||||
properties:
|
||||
solfeggioFrequencyId:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 9
|
||||
colors:
|
||||
type: array
|
||||
description: Seven hex colors, ordered light to dark.
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
pattern: '^#[0-9A-Fa-f]{6}$'
|
||||
Proclamation:
|
||||
type: object
|
||||
required: [id, name, content, categoryId, groupId, solfeggioFrequencyId]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
description: HTML content.
|
||||
categoryId:
|
||||
type: integer
|
||||
groupId:
|
||||
type: integer
|
||||
solfeggioFrequencyId:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 9
|
||||
colorPalette:
|
||||
type: array
|
||||
description: Present only when a palette exists for the canonical frequency id.
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
pattern: '^#[0-9A-Fa-f]{6}$'
|
||||
LicenseRequest:
|
||||
type: object
|
||||
required: [license_key, email_address]
|
||||
properties:
|
||||
license_key:
|
||||
type: string
|
||||
pattern: '^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$'
|
||||
example: ABCD-EFGH-IJKL-MNOP
|
||||
email_address:
|
||||
type: string
|
||||
format: email
|
||||
maxLength: 254
|
||||
LicenseResponse:
|
||||
type: object
|
||||
required: [key, email_address]
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
example: ABCD-EFGH-IJKL-MNOP
|
||||
email_address:
|
||||
type: string
|
||||
format: email
|
||||
OnboardingRequest:
|
||||
type: object
|
||||
required: [email]
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
maxLength: 254
|
||||
name:
|
||||
type: string
|
||||
maxLength: 120
|
||||
description: Optional display name. Defaults to the email local-part.
|
||||
OnboardingResponse:
|
||||
type: object
|
||||
required: [success, message, user_id]
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
example: Account and license key created
|
||||
user_id:
|
||||
type: string
|
||||
ErrorMessage:
|
||||
type: object
|
||||
required: [message]
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
@@ -1,191 +0,0 @@
|
||||
# Portal API v2 — current schema
|
||||
|
||||
The kiosk talks to `https://portal.radiantwavetech.com/api/v2`. This document
|
||||
describes the current contract after the move to an **active/inactive license
|
||||
model**. There are no subscription levels: a license is either **valid**
|
||||
(subscribed) or not, and the kiosk treats a successful `POST /license`
|
||||
validation as the device's "subscribed" flag.
|
||||
|
||||
Base URL: `https://portal.radiantwavetech.com/api/v2`
|
||||
|
||||
Authentication: `Authorization: Bearer sk_<hex>` on every request (the API key
|
||||
is baked into the kiosk binary at build time).
|
||||
|
||||
---
|
||||
|
||||
## `POST /license`
|
||||
|
||||
Validates a license key / email pair.
|
||||
|
||||
Request:
|
||||
|
||||
```
|
||||
POST /api/v2/license
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer sk_<hex>
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"license_key": "XXXX-XXXX-XXXX-XXXX",
|
||||
"email_address": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
Response (200):
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "XXXX-XXXX-XXXX-XXXX",
|
||||
"email_address": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
Contract (kiosk `parseLicenseJson`):
|
||||
|
||||
- `key`: string, **required non-empty** (empty key ⇒ parse failure).
|
||||
- `email_address`: string.
|
||||
- Any other fields (e.g. the legacy `subscription_level`) are ignored. A 2xx
|
||||
response means the license is valid and the subscription is **active**; the
|
||||
kiosk stores no subscription state other than the `license_last_verified`
|
||||
stamp that drives its 7-day offline grace window.
|
||||
|
||||
Errors:
|
||||
|
||||
| Code | Body | Meaning |
|
||||
|------|------|---------|
|
||||
| 200 | success object | License valid and owned by the email |
|
||||
| 400 | `{"message": "license_key is required"}` | Missing `license_key` |
|
||||
| 400 | `{"message": "email_address is required"}` | Missing `email_address` |
|
||||
| 400 | `{"message": "license key not found"}` | No key with that value |
|
||||
| 400 | `{"message": "license key is not active"}` | Key exists but inactive |
|
||||
| 400 | `{"message": "user does not exist"}` | No user with that email |
|
||||
| 400 | `{"message": "key does not belong to specified user"}` | Key owned by a different user |
|
||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
||||
|
||||
---
|
||||
|
||||
## `GET /proclamations`
|
||||
|
||||
Lists every proclamation. There is **no `subscriptionLevelId`** — any validated
|
||||
license may access any proclamation.
|
||||
|
||||
Response (200):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "b40fdcb9-703c-420e-af32-a081964c6f08",
|
||||
"name": "Peace & Relaxation",
|
||||
"content": "<p>The text content, HTML formatted</p>",
|
||||
"categoryId": 5,
|
||||
"solfeggioFrequencyId": 5,
|
||||
"colorPalette": ["#32CD32", "#3CB371", "#90EE90", "#98FB98", "#ADFFA9", "#C8FFD2", "#F0FFF0"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Contract (kiosk `parseProclamationsJson` + `parseProclamationPalettesJson`):
|
||||
|
||||
- `id`: string UUID, **required non-empty** (empty ids are skipped).
|
||||
- `name`: string, used as the display title. Kiosk strips HTML from it.
|
||||
- `content`: string, **HTML** (the kiosk strips tags and unescapes entities
|
||||
before rendering).
|
||||
- `categoryId`: int, maps to a `/categories` id.
|
||||
- `solfeggioFrequencyId`: int, **must be a canonical id (1–9)**.
|
||||
- `colorPalette` (optional): array of exactly 7 `"#RRGGBB"` strings. When
|
||||
absent, the kiosk falls back to `/color-palettes`, then to its own 528 Hz
|
||||
seed.
|
||||
- Legacy `subscriptionLevelId` fields in the payload are ignored.
|
||||
|
||||
Errors:
|
||||
|
||||
| Code | Body | Meaning |
|
||||
|------|------|---------|
|
||||
| 200 | array of proclamation records | Success |
|
||||
| 400 | `{"message": "not-authorized"}` | Entitlement headers supplied but key or email does not exist |
|
||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
||||
|
||||
---
|
||||
|
||||
## `GET /categories`
|
||||
|
||||
Lists the proclamation categories.
|
||||
|
||||
Response (200):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Spiritual",
|
||||
"parentId": 0,
|
||||
"sortOrder": 0
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Contract (kiosk `parseCategoriesJson`):
|
||||
|
||||
- `id`: int, **required non-zero** (zero ids are skipped).
|
||||
- `name`: string.
|
||||
- `parentId`: int, `null` maps to 0 (root).
|
||||
- `sortOrder`: int.
|
||||
|
||||
---
|
||||
|
||||
## `POST /automations/create/account`
|
||||
|
||||
Creates (or reuses) a user account and issues a license key. **No
|
||||
`subscription_level` is sent** — every created license is a standard active
|
||||
license.
|
||||
|
||||
Request:
|
||||
|
||||
```
|
||||
POST /api/v2/automations/create/account
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer sk_<hex>
|
||||
X-Timestamp: <unix seconds>
|
||||
X-Nonce: <random uuid>
|
||||
X-Signature: <hex HMAC-SHA256 of canonical request with the onboarding secret>
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"name": "Optional Display Name"
|
||||
}
|
||||
```
|
||||
|
||||
- `email` (required, string).
|
||||
- `name` (optional, string; defaults to the local part of `email`).
|
||||
|
||||
Response (200):
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Account and license key created",
|
||||
"user_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
Errors:
|
||||
|
||||
| Code | Body | Meaning |
|
||||
|------|------|---------|
|
||||
| 200 | success object | Account (+ license key) created |
|
||||
| 400 | `{"message": "email is required"}` | Missing/empty `email` |
|
||||
| 401 | `{"error": "invalid signature"}` | Bad/expired/replayed HMAC signature |
|
||||
| 429 | `{"message": "rate limit exceeded"}` | Too many requests from this IP/email |
|
||||
| 500 | `{"message": "Failed to create account"}` | User creation failed |
|
||||
|
||||
---
|
||||
|
||||
## Readiness routes (not yet wired into the app)
|
||||
|
||||
- `GET /color-palettes` → `[{solfeggioFrequencyId, colors[7]}]`
|
||||
- `GET /frequencies` → canonical id 1–9 + `value` in Hz
|
||||
|
||||
These exist for future sync steps; the kiosk does not call them yet.
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
Radiant Wave
|
||||
User Manual
|
||||
Version 1.0
|
||||
|
||||
Intro
|
||||
Some thank you message and introduction to Radiant Waves.
|
||||
|
||||
Table of Contents
|
||||
|
||||
Setup
|
||||
Connect to WiFi
|
||||
Connection to the internet is required for Radiant Wave to operate.
|
||||
|
||||
Wifi screen appears first
|
||||
UP/DOWN to navigate through detected WiFi networks
|
||||
Enter to select one
|
||||
Password prompt if applicable
|
||||
|
||||
I still need to test entering an incorrect WiFi password on a kiosk machine
|
||||
Check for crashes or correct error messaging
|
||||
|
||||
Email Address
|
||||
|
||||
The next screen you’ll be presented with is the email address prompt.
|
||||
|
||||
When you purchased your RadiantWave system, you should have received an email at the address you provided during your purchase containing your license key.
|
||||
|
||||
Enter the email address where you received your license key here.
|
||||
|
||||
Note that you will not be able to enter an invalid email address, but you will be able to enter the incorrect email address. You will see how to change your email address later.
|
||||
|
||||
License Key
|
||||
|
||||
Grab your license key from your email, now is the time to use it!
|
||||
|
||||
A few notes on the License Key prompt:
|
||||
You don't have to hold SHIFT or enable CAPS LOCK, because that's already done for you
|
||||
You won't have to enter the dashes "-", as those are entered for you
|
||||
You CAN enter an incorrect license key
|
||||
You CAN fix it later
|
||||
|
||||
Same steps as the email address, just type out your license key and press enter.
|
||||
|
||||
The Main Menu
|
||||
|
||||
Now you're in! This is the main menu, and it doesn't do a whole lot. It's only purpose is to display some system information for you to quickly reference when needed, such as the email address and license key you currently have saved or the current system version you have running. Other than that, the Main Menu's only purpose is to provide navigation between the system settings, and the main attraction itself.
|
||||
|
||||
TODO
|
||||
|
||||
wifi
|
||||
email
|
||||
license
|
||||
main menu
|
||||
settings
|
||||
time zone
|
||||
date
|
||||
logs
|
||||
audio options
|
||||
proclamations
|
||||
play
|
||||
updates
|
||||
Reference in New Issue
Block a user