PF-WATCHDOG-001 product endpoints.
All paths are relative to http://<device-ip>/api/v1.
For platform base endpoints (device info, relay, power, telemetry, config, OTA), see the Platform REST API Reference.
| Version | Date | Notes |
|---|---|---|
| 1.0 | 2026-02-11 | Initial release |
The Internet Watchdog exposes 7 product-specific REST endpoints under the /watchdog/ namespace. The platform prepends /api/v1 automatically, so the product declares paths like /watchdog/status and they become accessible at /api/v1/watchdog/status.
http://<device-ip>/api/v1
The device runs an HTTP server on port 80. No authentication. LAN-only.
| Method | Path | Description |
|---|---|---|
| GET | /watchdog/status | Current monitoring status, stats, and timestamps |
| GET | /watchdog/config | All configuration parameters |
| POST | /watchdog/config | Update configuration (partial) |
| POST | /watchdog/reboot | Trigger manual router reboot |
| GET | /watchdog/history | Event history (last 16 events) |
| GET | /watchdog/ping | Force connectivity check with per-target results |
| POST | /watchdog/stats/reset | Reset cumulative statistics to zero |
/watchdog/statusReturns the full current state of the watchdog — state machine state, connectivity, cumulative stats, and timestamps.
200 OK{
"state": "monitoring",
"internet_up": true,
"enabled": true,
"relay": true,
"reboot_count": 0,
"total_reboots": 3,
"total_outages": 3,
"uptime_percent": 99.7,
"consecutive_fails": 0,
"fail_threshold": 3,
"scheduled_reboot": null,
"last_check": "2026-02-11T15:30:00Z",
"last_outage": "2026-02-10T08:15:00Z",
"last_reboot": "2026-02-10T08:17:30Z"
}
| Field | Type | Description |
|---|---|---|
state | string | State machine state (see table below) |
internet_up | boolean | true if last ping round had at least one successful target |
enabled | boolean | Master enable/disable switch |
relay | boolean | Relay state: true = closed (power on), false = open (power off) |
reboot_count | integer | Reboots in the current failure cycle. Resets to 0 when internet is restored. |
total_reboots | integer | Cumulative lifetime reboots (persisted in NVS) |
total_outages | integer | Cumulative lifetime outages detected (persisted in NVS) |
uptime_percent | float | 100 × (1 − total_downtime_s / total_monitoring_s). Returns 100.0 if no monitoring time recorded. |
consecutive_fails | integer | Current streak of consecutive ping failures. Resets to 0 on any success. |
fail_threshold | integer | How many consecutive failures trigger an outage (convenience copy from config) |
scheduled_reboot | string | null | "HH:MM" if daily scheduled reboot is configured, otherwise null |
last_check | string | null | ISO 8601 UTC timestamp of the most recent ping check, or null if none yet |
last_outage | string | null | ISO 8601 UTC timestamp of the most recent outage detection, or null |
last_reboot | string | null | ISO 8601 UTC timestamp of the most recent router reboot, or null |
| State | Description |
|---|---|
monitoring | Normal operation — periodically pinging targets |
grace_period | Outage detected — waiting before power-cycling |
rebooting | Relay is open — router power is off |
post_reboot_grace | Router recently rebooted — pinging periodically, waiting for internet to return |
cooldown | Waiting between retry attempts after a failed reboot |
max_retries_exceeded | All retries exhausted — stopped rebooting, requires manual intervention |
disabled | Monitoring is turned off |
"2026-02-11T15:30:00Z". A null value means the event hasn't occurred since boot (or the system clock hasn't synced — epoch before 2025-01-01).uptime_percent reflects cumulative monitoring time, not just the current session./watchdog/configReturns all current configuration parameters with their active values.
200 OK{
"enabled": true,
"ping_interval_s": 60,
"fail_threshold": 3,
"grace_before_reboot_s": 120,
"power_off_duration_s": 30,
"post_reboot_grace_s": 300,
"max_retries": 3,
"retry_cooldown_min": 30,
"scheduled_reboot": null,
"ping_targets": ["8.8.8.8", "1.1.1.1", "208.67.222.222"]
}
| Field | Type | Default | Range | Description |
|---|---|---|---|---|
enabled | boolean | true | — | Master monitoring switch |
ping_interval_s | integer | 60 | 30–300 | Seconds between ping rounds |
fail_threshold | integer | 3 | 2–10 | Consecutive failures before declaring outage |
grace_before_reboot_s | integer | 120 | 0–600 | Seconds to wait after outage before power cycle |
power_off_duration_s | integer | 30 | 10–120 | Seconds router stays powered off |
post_reboot_grace_s | integer | 300 | 120–900 | Max seconds to wait for internet after router reboot (exits early on success) |
max_retries | integer | 3 | 1–10 | Maximum power-cycle attempts before giving up |
retry_cooldown_min | integer | 30 | 10–120 | Minutes between retry attempts |
scheduled_reboot | string | null | null | "HH:MM" or null | Daily preventative reboot time (24h format), or null to disable |
ping_targets | string[] | ["8.8.8.8", "1.1.1.1", "208.67.222.222"] | 1–4 IPv4 addresses | Targets to ping each round. ALL must fail for a failure to count. |
/watchdog/configUpdate configuration. Accepts a partial JSON body — only fields present in the request are changed. Updated values are persisted to NVS immediately and take effect on the next monitoring cycle.
Returns the full updated config on success (same format as GET /watchdog/config).
JSON object with any subset of the config fields. Maximum body size: 512 bytes.
Example — change ping interval and add a scheduled reboot:
{
"ping_interval_s": 120,
"scheduled_reboot": "04:00"
}
Example — update ping targets:
{
"ping_targets": ["8.8.8.8", "1.1.1.1"]
}
Example — reset ping targets to defaults:
{
"ping_targets": null
}
Example — disable scheduled reboot:
{
"scheduled_reboot": null
}
| Field | Rule |
|---|---|
ping_interval_s | Integer, 30–300 |
fail_threshold | Integer, 2–10 |
grace_before_reboot_s | Integer, 0–600 |
power_off_duration_s | Integer, 10–120 |
post_reboot_grace_s | Integer, 120–900 |
max_retries | Integer, 1–10 |
retry_cooldown_min | Integer, 10–120 |
scheduled_reboot | "HH:MM" string (00:00–23:59), or null to disable |
ping_targets | Array of 1–4 valid IPv4 address strings, or null to reset to defaults |
enabled | Boolean. Changing to true enters monitoring; false enters disabled and ensures relay is on. |
200 OKFull config object (same as GET /watchdog/config).
| Status | Body | Condition |
|---|---|---|
| 400 | "Empty body" | No request body |
| 400 | "Invalid JSON" | Body is not valid JSON |
| 400 | "<field> must be <min>–<max>" | Numeric field out of range |
| 400 | "<field> must be a number" | Non-numeric value for numeric field |
| 400 | "ping_targets must be an array" | Wrong type for ping_targets |
| 400 | "ping_targets must have 1-4 entries" | Array too long or empty |
| 400 | "ping_targets entries must be non-empty strings" | Non-string element |
| 400 | "ping_targets entry too long" | Element exceeds 40 characters |
| 400 | "ping_targets entry is not a valid IPv4 address" | Invalid IP format |
| 400 | "scheduled_reboot must be \"HH:MM\" or null" | Wrong type |
| 400 | "scheduled_reboot must be \"HH:MM\" (00:00–23:59)" | Invalid time value |
enabled to true: pushes enabled event to history, enters monitoring state, resets consecutive_fails and reboot_count.enabled to false: pushes disabled event to history, enters disabled state, turns relay on (ensures router stays powered)./watchdog/rebootTrigger a manual router power-cycle. The watchdog opens the relay (power off), waits power_off_duration_s, closes the relay (power on), then enters post_reboot_grace state.
None (ignored if present).
200 OK{
"status": "rebooting"
}
| Status | Body | Condition |
|---|---|---|
| 409 | "Already rebooting" | Device is in rebooting or post_reboot_grace state |
manual_reboot event to history.last_reboot timestamp./watchdog/historyReturns the event ring buffer — up to 16 most recent events, ordered oldest first.
200 OK[
{
"event": "device_online",
"timestamp_ms": 1500,
"reboot_count": 0
},
{
"event": "outage_detected",
"timestamp_ms": 185000,
"reboot_count": 0
},
{
"event": "reboot_started",
"timestamp_ms": 305000,
"reboot_count": 1
}
]
| Field | Type | Description |
|---|---|---|
event | string | Event type (see table below) |
timestamp_ms | integer | Milliseconds since device boot (not epoch time) |
reboot_count | integer | Number of reboots at the time of this event |
| Event | Description |
|---|---|
device_online | Device booted and started monitoring |
outage_detected | All ping targets failed for fail_threshold consecutive rounds |
reboot_started | Router power-cycle initiated (relay opened) |
internet_restored | Connectivity recovered after an outage |
max_retries_exceeded | All retry attempts exhausted — watchdog stopped rebooting |
scheduled_reboot | Daily scheduled reboot triggered |
manual_reboot | User-initiated reboot (via REST, MCP, MQTT, or button) |
enabled | Monitoring was enabled |
disabled | Monitoring was disabled |
timestamp_ms is relative to device boot, not wall-clock time. Use last_check/last_outage/last_reboot in /watchdog/status for absolute timestamps.device_online event at each boot./watchdog/pingForce an immediate connectivity check against all configured ping targets. Returns per-target results.
200 OK{
"internet_up": true,
"targets": [
{ "ip": "8.8.8.8", "success": true, "rtt_ms": 12 },
{ "ip": "1.1.1.1", "success": true, "rtt_ms": 8 },
{ "ip": "208.67.222.222", "success": false, "rtt_ms": 0 }
]
}
| Field | Type | Description |
|---|---|---|
internet_up | boolean | true if at least one target responded |
targets | array | Per-target results |
targets[].ip | string | IPv4 address that was pinged |
targets[].success | boolean | Whether this target responded |
targets[].rtt_ms | integer | Round-trip time in milliseconds (0 if failed) |
internet_up state.last_check timestamp.consecutive_fails or trigger state transitions — this is a diagnostic check, not a monitoring cycle./watchdog/stats/resetReset all cumulative statistics to zero. This clears lifetime counters and timestamps.
None (ignored if present).
200 OK{
"status": "stats_reset"
}
| Stat | Reset to |
|---|---|
total_reboots | 0 |
total_outages | 0 |
uptime_percent | 100.0 (monitoring time counters reset) |
last_check | null |
last_outage | null |
last_reboot | null |
application/json.application/json (or no Content-Type for empty-body endpoints like /watchdog/reboot and /watchdog/stats/reset).text/plain.Validation errors return HTTP 400 with a plain-text body describing the problem. There is no structured error JSON — the body is the error message string.
The device is single-threaded for HTTP request handling. Requests are processed sequentially. Long-running operations (like /watchdog/ping, which performs ICMP pings) block the HTTP server until complete.
The device obtains wall-clock time via SNTP after WiFi connects. Timestamp fields return null if the system clock hasn't been set yet (epoch before 2025-01-01).