API
If you are a developer and want to integrate clock configuration into your app, Home Assistant, Node-RED, scripts, or another system, use this API reference.
This document covers two independent API categories:
- The Cloud Discovery API, hosted on a public server
- The Device API, provided by the clock's ESP32 on the local network
These APIs use different hosts, transport protocols, response formats, and CORS policies. Do not mix them.
1. API Categories
| Category | Service Location | Base URL | Response Format | CORS | Primary Use |
|---|---|---|---|---|---|
| Cloud Discovery API | Public server | https://topyuan.top/clock/findapi | JSON response body | Allows any origin: * | Obtain a clock's local IP address by matching its public egress IP |
| Device API | Clock ESP32 | http://<clock-local-ip> | Configuration data in HTTP response headers; response body is usually empty | No CORS headers | Read configuration, change settings, read ADC values, restart the device, or erase Wi-Fi credentials |
Recommended call sequence: first request the Cloud Discovery API to obtain candidate localIp values, then request http://<localIp>/get or another Device API endpoint from the current local network.
If you can obtain the clock's local IP address by another method, you do not need to call the Cloud Discovery API.
2. Cloud Discovery API
2.1 General Conventions
- Full URL:
https://topyuan.top/clock/findapi - Service location: public server, not the clock's ESP32
- Transport: HTTPS
- Method:
GET - Authentication: none
- Successful response:
200 OKwith a JSON array - Content type:
application/json; charset=utf-8 - CORS:
Access-Control-Allow-Origin: * - Cache policy:
Cache-Control: no-store
2.2 Discover Devices: GET /clock/findapi
An app or other client can call this endpoint first to obtain the IP addresses of clocks that may be on the current local network. It can then use each returned localIp to call the device's /get, /set, and other endpoints.
2.3 How It Works
Each clock periodically reports its public IP address, local IP address, and device information to the server. The discovery endpoint uses the caller's REMOTE_ADDR as its public IP address and queries for devices that meet all of the following conditions:
- The public IP reported by the device matches the caller's public IP
- The device has reported within the last 12 hours
- The device reported a valid local IPv4 address
Results are ordered by the most recent report time, newest first.
2.4 Request
GET /clock/findapi.php HTTP/1.1
Host: topyuan.topThe request has no query parameters or body. The server derives the public IP address from the current connection; a client cannot specify a public IP address to query.
curl -i https://topyuan.top/clock/findapi2.5 Successful Response
- Status:
200 OK Content-Type: application/json; charset=utf-8Access-Control-Allow-Origin: *- Body: JSON array
- Cache policy:
Cache-Control: no-store
[
{
"chipId": "9730432",
"localIp": "192.168.31.247",
"deviceType": "ClockWise Plus",
"lastSeen": "2026-08-04 15:26:30"
},
{
"chipId": "10557104",
"localIp": "192.168.31.180",
"deviceType": "SuperY",
"lastSeen": "2026-08-04 15:20:12"
}
]| JSON Field | Type | Description |
|---|---|---|
chipId | String | Clock chip ID |
localIp | String | Local IPv4 address reported by the clock, for example 192.168.1.50 |
deviceType | String | ClockWise Plus, SuperY, or SuperY Lite |
lastSeen | String | Time of the last report recorded by the server, in YYYY-MM-DD HH:mm:ss format |
The Device API documented on this page applies to devices whose deviceType is ClockWise Plus. If you also use my other clock models, the Cloud Discovery API may return those device types as well.
If no devices are found, the endpoint returns an empty array:
[]2.6 Error Responses
| Status | Example Response | Meaning |
|---|---|---|
400 | {"error":"invalid_client_ip"} | The server could not obtain a valid client IP address |
405 | {"error":"method_not_allowed"} | A method other than GET or OPTIONS was used |
500 | {"error":"discovery_unavailable"} | Server error |
2.7 Discovery Limitations
A matching public IP address only indicates that devices may be on the same local network; it is not definitive proof. Carrier-grade NAT (CGNAT), enterprise networks, campus networks, VPNs, or proxies may cause unrelated clients to share a public IP address. Conversely, different IPv4 and IPv6 egress paths may prevent devices on the same local network from matching.
This endpoint enables wildcard CORS, so browser-based web apps can call it cross-origin. It accepts GET and OPTIONS; browser preflight requests receive 204 No Content.
3. Device API Conventions
This section and all subsequent device endpoints are served by the clock's ESP32. They are independent of the public Cloud Discovery API described in Section 2.
3.1 Connection and Data Format
- Base URL:
http://<clock-local-ip>, for examplehttp://192.168.1.50 - Service location: clock ESP32
- Port:
80 - Transport: HTTP; HTTPS is not supported
- Authentication: none
- Write format:
application/x-www-form-urlencoded; JSON request bodies are not supported - Successful response: read and control endpoints usually return
204 No Contentwith an empty body - Character encoding: string parameters use UTF-8 and must be URL-encoded
- CORS: the firmware does not return CORS headers
Security notice: Any client that can reach the clock's local IP address can change settings, restart the device, or erase its Wi-Fi credentials. Expose these endpoints only on a trusted local network. Never forward them directly to the public internet.
3.2 Why Data Is Returned in HTTP Headers
Because the ESP32 has constrained runtime memory and response-buffer capacity, and JSON encoding adds processing overhead, the firmware does not use a conventional JSON response body. Instead, it places configuration values directly in HTTP response headers and returns an empty 204 No Content response. This is an implementation trade-off for a resource-constrained embedded device, not a conventional REST API design. Third-party clients must read the response headers and must not rely on a response body or JSON parsing.
3.3 Device Endpoint Overview
All paths in the following table are relative to http://<clock-local-ip>:
| Method | Path | Purpose | Successful Response |
|---|---|---|---|
GET | /get | Read all current configuration and device information | 204; data is in the response headers |
POST | /set | Change one or more configuration values | 204; no response body |
GET | /read?pin=<GPIO> | Read the ADC value of a specified GPIO | 204; result is in the pin response header |
POST | /restart | Restart the device immediately | 204; the connection then closes |
POST | /erase | Erase the Wi-Fi SSID and password, then restart | 204; the connection then closes |
3.4 Important Compatibility Notes
- HTTP header names are case-insensitive. Some clients automatically convert
displayBrighttodisplaybright; clients must perform case-insensitive header lookups. - Data from
/getand/readis returned in response headers. The response body is always empty; do not attempt to parse it as JSON. - The device firmware does not return CORS headers. A web page loaded from a different origin—another domain, port, or protocol—cannot read these responses directly in a browser. Native apps, backend services, command-line clients, and the clock's own web UI are not subject to this restriction.
/setdoes not return per-field validation results. Unknown fields are ignored; some invalid numeric values are corrected, while others may be converted to0or truncated. Call/getafter writing to verify the effective values.- A
+in form data is interpreted as a space. For example, the+8:00time-zone offset must be encoded as%2B8%3A00. Usecurl --data-urlencodeto encode values automatically. /setsupports partial updates: omitted fields remain unchanged. Fields whose values are empty strings are also treated as omitted, so the current API cannot clear a string-valued setting.
4. Read All Configuration: GET /get
Request
GET /get HTTP/1.1
Host: 192.168.1.50The request has no parameters or body.
Response
The status is 204 No Content, and all data is returned in custom response headers. This avoids the additional processing and memory overhead of generating and buffering a JSON response on the ESP32. For example:
HTTP/1.1 204 No Content
displayBright: 205
autoBrightMin: 30
autoBrightMax: 2000
wifiSsid: MyWiFi
ntpFailRestart: 1
clockFace: 1
autoChange: 1
autoInterval: 0
version: 4.2The complete header list follows. A default value is the firmware default used on first boot or when that setting has not yet been saved.
| Response Header | Type | Meaning and Values | Default | Corresponding /set Field |
|---|---|---|---|---|
displayBright | Integer | Display brightness, 0–255. Used as the daytime or maximum brightness in automatic and scheduled modes | 205 | displayBright |
autoBrightMin | Integer | Nighttime LDR threshold for automatic brightness, 1–300 | 30 | Combined with autoBrightMax as autoBright |
autoBrightMax | Integer | Bright-environment LDR threshold for automatic brightness, 800–4095 | 2000 | Combined with autoBrightMin as autoBright |
specialLed | Enum integer | LED panel color order: 0 RGB, 1 RBG, 2 GBR | 0 | specialLed |
use24hFormat | Boolean integer | 1 for 24-hour time; 0 for 12-hour time | 1 | use24hFormat |
ldrPin | Integer | GPIO connected to the photoresistor; the current hardware page specifies GPIO 35 | 35 | ldrPin |
wifiSsid | String | SSID of the Wi-Fi network to which the device is currently connected | Current connection | Read-only |
ntpServer | String | NTP server hostname or IP address | ntp2.aliyun.com | ntpServer |
ntpFailRestart | Boolean integer | 1 enables restart after repeated NTP failures; 0 disables it. After at least one successful synchronization, 24 consecutive failed synchronization attempts restart the device; any successful synchronization resets the failure count | 1 | ntpFailRestart |
displayRotation | Enum integer | Display rotation: 0=0°, 1=90°, 2=180°, 3=270° | 0 | displayRotation |
clockFace | Enum integer | Current clock-face number, 1–27; see the clock-face table | 1 | clockFace |
language | Enum integer | Configuration UI language: 0 Chinese, 1 English | 0 | language |
totalYear | Integer | Accumulated runtime, years component | 0 | Read-only |
totalMonth | Integer | Accumulated runtime, months component | 0 | Read-only |
totalDay | Integer | Accumulated runtime, days component | 0 | Read-only |
brightMethod | Enum integer | Brightness mode: 0 ambient-light adjustment, 1 scheduled adjustment, 2 fixed brightness | 0 | brightMethod |
nightLevel | Integer | Nighttime brightness level in scheduled mode, 1–5 | 1 | nightLevel |
nightStarth | Integer | Hour when the nighttime period starts; recommended range 0–23 | 22 | nightStarth |
nightStartm | Integer | Minute when the nighttime period starts; recommended range 0–59 | 0 | nightStartm |
nightEndh | Integer | Hour when the nighttime period ends; recommended range 0–23 | 8 | nightEndh |
nightEndm | Integer | Minute when the nighttime period ends; recommended range 0–59 | 0 | nightEndm |
sqtext | String | Display value for a fixed UTC offset, such as +8:00 or -3:30; used when timemode=0 | +8:00 | sqtext |
timemode | Enum integer | Time-zone mode: 0 fixed UTC offset, 1 POSIX time zone with daylight-saving rules | 0 | timemode |
posix | String | POSIX TZ string passed to the clock library | <+8>-8 | posix |
autoChange | Enum integer | Automatic clock-face mode: 0 disabled, 1 sequential, 2 random; the schedule is controlled by autoInterval | 1 | autoChange |
autoInterval | Integer | Automatic clock-face interval in minutes; effective only when autoChange is not 0. 0 means daily at 00:00; other valid values are multiples of 10 from 10 through 1440. This HTTP field corresponds to the internal firmware setting autoChangeInterval | 0 | autoInterval |
faceControl | String | 27-character clock-face enable mask; left to right corresponds to faces 1–27, where 1 enables and 0 disables a face | 27 1 characters | faceControl |
reversePhase | Boolean integer | HUB75 clock phase: 1 inverted, 0 normal | 0 | reversePhase |
nightMode | Enum integer | Nighttime behavior: 0 none, 1 turn off the LED panel, 2 show the oversized clock | 2 | nightMode |
superColor | Integer | Oversized-clock color as a decimal RGB565 value, 0–65535 | 16936 | superColor |
version | String | Current firmware version | Current version | Read-only |
Command-line example:
curl -i http://192.168.1.50/get5. Change Configuration: POST /set
Request Format
POST /set HTTP/1.1
Host: 192.168.1.50
Content-Type: application/x-www-form-urlencoded
displayBright=180&use24hFormat=1A single request may include one or more fields. On success, the endpoint returns 204 No Content with no response body and does not echo the resulting configuration in custom headers.
Writable Fields
| Form Field | Input Format | Valid/Recommended Values | Description |
|---|---|---|---|
displayBright | Decimal integer | 0–255 | Set display brightness |
autoBright | Fixed-format string | MMMM,XXXX | Set the minimum and maximum LDR thresholds together; both values must be four digits, for example 0030,2000 |
specialLed | Integer | 0, 1, or 2 | RGB / RBG / GBR panel color order |
reversePhase | Boolean string | 0 or 1 | Invert the panel phase |
use24hFormat | Boolean string | 0 or 1 | Use 24-hour time |
ldrPin | Integer | 35 on the current hardware | Change the photoresistor GPIO; the firmware does not verify that the pin supports ADC |
ntpServer | String | Valid NTP hostname or IP address | Synchronize immediately after changing the server |
ntpFailRestart | Boolean string | 0 or 1 | Restart after 24 consecutive NTP synchronization failures; failures are counted only after the device has synchronized successfully at least once |
displayRotation | Integer | 0–3 | Represent 0°, 90°, 180°, and 270°, respectively |
clockFace | Integer | 1–27 | Switch the current clock face |
language | Integer | 0 or 1 | Change the configuration UI language; takes effect after reopening the configuration page |
brightMethod | Integer | 0, 1, or 2 | Automatic, scheduled, or fixed brightness |
nightLevel | Integer | 1–5 | Values outside the range are changed to 1 |
nightStarth | Integer | 0–23 | Nighttime start hour; the firmware does not range-check this field |
nightStartm | Integer | 0–59 | Nighttime start minute; the firmware does not range-check this field |
nightEndh | Integer | 0–23 | Nighttime end hour; the firmware does not range-check this field |
nightEndm | Integer | 0–59 | Nighttime end minute; the firmware does not range-check this field |
sqtext | URL-encoded string | For example +8:00 | Display value for fixed-offset mode; submit it together with posix |
timemode | Integer | 0 or 1 | Fixed offset or daylight-saving-aware mode |
posix | URL-encoded string | Valid POSIX TZ string | The firmware does not validate the syntax; validate it in the client using the regular expression below |
autoChange | Integer | 0, 1, or 2 | Disable, sequential, or random clock-face switching |
autoInterval | Integer | 0, or a multiple of 10 from 10 through 1440 | Automatic clock-face interval in minutes; effective only when autoChange is not 0, and 0 means daily at 00:00. This HTTP field corresponds to the internal firmware setting autoChangeInterval |
faceControl | 27-character string | Only 0 and 1 | Character N controls clock face N; enabling at least two faces is recommended |
nightMode | Integer | 0, 1, or 2 | No action, turn off the display, or show the oversized clock |
superColor | Decimal integer | 0–65535 | RGB565 color for the oversized clock |
wifiSsid, totalYear, totalMonth, totalDay, and version are read-only; submitting them to /set has no effect. autoBrightMin and autoBrightMax also cannot be submitted independently; use the combined autoBright field.
Regular expression for validating posix:
^([a-zA-Z]{1,6}|<[a-zA-Z0-9+-]{1,6}>)([+-]?([0-9]|1[0-4])(:[0-5]\d)?)((([a-zA-Z]{1,6}|<[a-zA-Z0-9+-]{1,6}>)([+-]?([0-9]|1[0-4])(:[0-5]\d)?)?)(,M([1-9]|1[0-2]).([1-5]).([0-6])(/([0-9]|1[0-9]|2[0-4])(:[0-5]\d)?)?,M([1-9]|1[0-2]).([1-5]).([0-6])(/([0-9]|1[0-9]|2[0-4])(:[0-5]\d)?)?)?)?$
5.1 Common Write Examples
Set brightness and 24-hour time:
curl -i -X POST http://192.168.1.50/set \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "displayBright=180&use24hFormat=1"Set automatic-brightness thresholds. The exact autoBright format is a four-digit minimum, an ASCII comma, and a four-digit maximum:
curl -i -X POST http://192.168.1.50/set \
--data-urlencode "autoBright=0030,2000"Set the NTP server and enable restart after 24 consecutive synchronization failures:
curl -i -X POST http://192.168.1.50/set \
--data-urlencode "ntpServer=ntp2.aliyun.com" \
--data "ntpFailRestart=1"Set a fixed UTC+8 time zone. Submit all three related fields in the same request:
curl -i -X POST http://192.168.1.50/set \
--data-urlencode "sqtext=+8:00" \
--data-urlencode "timemode=0" \
--data-urlencode "posix=<+8>-8"Set a POSIX time zone with daylight-saving rules:
curl -i -X POST http://192.168.1.50/set \
--data-urlencode "timemode=1" \
--data-urlencode "posix=EST5EDT,M3.2.0,M11.1.0"Set the nighttime period to 22:30–07:00 and turn off the display at night:
curl -i -X POST http://192.168.1.50/set \
--data "nightLevel=1&nightStarth=22&nightStartm=30&nightEndh=7&nightEndm=0&nightMode=1"Enable sequential automatic clock-face switching and allow only faces 1, 2, and 3 in the rotation:
curl -i -X POST http://192.168.1.50/set \
--data "autoChange=1&faceControl=111000000000000000000000000"Change the clock face sequentially every 180 minutes:
curl -i -X POST http://192.168.1.50/set \
--data "autoChange=1&autoInterval=180"Restore automatic clock-face switching to the daily 00:00 schedule:
curl -i -X POST http://192.168.1.50/set \
--data "autoInterval=0"5.2 Firmware Coercion and Clamping Behavior
autoBrightis parsed at fixed character offsets: the first four characters are the minimum and characters 6–9 are the maximum. Submit exactly nine characters in the form0030,2000.autoBrightMinvalues below1are changed to1; values above300are changed to300.autoBrightMaxvalues below800are changed to800; values above4095are changed to4095.- A
nightLeveloutside1–5is changed to1. - Only the string
1enablesntpFailRestart; any other non-empty value disables it. Submit only0or1. /setacceptsautoIntervalonly when it is0or a multiple of 10 from10through1440. An invalid value is ignored and the existing setting is retained. If an invalid saved value is loaded at startup, the firmware changes the in-memory value to0.- A nonzero
autoIntervalis a relative interval. Its timer restarts when the device boots, when the automatic mode or interval changes, and after a manual clock-face change. - Most other integer parameters are not range-checked; clients must ensure that values are valid.
- Unknown and read-only fields do not produce an error; the response may still be
204.
6. Read ADC: GET /read
Reads the raw analogRead() value for a specified GPIO. This can be used to obtain the ADC reading from the photoresistor.
Input
| Name | Type | Required | Description |
|---|---|---|---|
pin | Integer | Yes | GPIO number; the current hardware connects the LDR to GPIO 35 by default |
Output
- Status:
204 No Content - Response body: empty
- Response header:
pin: <raw-ADC-value>
Example:
curl -i "http://192.168.1.50/read?pin=35"HTTP/1.1 204 No Content
pin: 1842The firmware converts the parameter to an unsigned 8-bit integer and passes it directly to analogRead() without checking whether the pin is valid. Third-party clients should not allow end users to supply an arbitrary GPIO number. Use the ldrPin returned by /get.
7. Restart the Device: POST /restart
This endpoint has no parameters or request body. The firmware attempts to send 204 and then immediately restarts.
curl -i -X POST http://192.168.1.50/restartThe connection closes immediately during restart. Some clients may report a network disconnection before receiving the complete response.
8. Erase Wi-Fi Credentials: POST /erase
This endpoint has no parameters or request body. It erases only the saved Wi-Fi SSID and password, then immediately restarts the device.
curl -i -X POST http://192.168.1.50/eraseAfter this call, the device is no longer reachable at its previous IP address and returns to the Wi-Fi provisioning flow. This is a destructive operation and should require confirmation before it is executed.
9. Clock-Face Numbers
| Number | Clock Face | Number | Clock Face |
|---|---|---|---|
| 1 | Super Mario | 15 | Shar Pei Dog |
| 2 | Pac Man | 16 | Girl |
| 3 | World Map | 17 | Kirby |
| 4 | Time In Words | 18 | Labubu-Zimomo |
| 5 | Clock Tower | 19 | Hello Kitty |
| 6 | Pokedex | 20 | Twinkle Twinkle |
| 7 | Retro Computer | 21 | Zootopia |
| 8 | Snoopy | 22 | Minecraft-Village |
| 9 | Nyan Cat | 23 | Codex |
| 10 | Transformer | 24 | Rainy Window |
| 11 | Minecraft-Torch | 25 | GTA VI |
| 12 | Coffee | 26 | Zelda-Sunrise |
| 13 | Pepsi | 27 | Particle Time |
| 14 | Pikachu |