Internet Watchdog — REST API Reference

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.

VersionDateNotes
1.02026-02-11Initial release


Overview

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.

Base URL

http://<device-ip>/api/v1

The device runs an HTTP server on port 80. No authentication. LAN-only.

Endpoints at a Glance

MethodPathDescription
GET/watchdog/statusCurrent monitoring status, stats, and timestamps
GET/watchdog/configAll configuration parameters
POST/watchdog/configUpdate configuration (partial)
POST/watchdog/rebootTrigger manual router reboot
GET/watchdog/historyEvent history (last 16 events)
GET/watchdog/pingForce connectivity check with per-target results
POST/watchdog/stats/resetReset cumulative statistics to zero

GET /watchdog/status

Returns the full current state of the watchdog — state machine state, connectivity, cumulative stats, and timestamps.

Response 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"
}

Fields

FieldTypeDescription
statestringState machine state (see table below)
internet_upbooleantrue if last ping round had at least one successful target
enabledbooleanMaster enable/disable switch
relaybooleanRelay state: true = closed (power on), false = open (power off)
reboot_countintegerReboots in the current failure cycle. Resets to 0 when internet is restored.
total_rebootsintegerCumulative lifetime reboots (persisted in NVS)
total_outagesintegerCumulative lifetime outages detected (persisted in NVS)
uptime_percentfloat100 × (1 − total_downtime_s / total_monitoring_s). Returns 100.0 if no monitoring time recorded.
consecutive_failsintegerCurrent streak of consecutive ping failures. Resets to 0 on any success.
fail_thresholdintegerHow many consecutive failures trigger an outage (convenience copy from config)
scheduled_rebootstring | null"HH:MM" if daily scheduled reboot is configured, otherwise null
last_checkstring | nullISO 8601 UTC timestamp of the most recent ping check, or null if none yet
last_outagestring | nullISO 8601 UTC timestamp of the most recent outage detection, or null
last_rebootstring | nullISO 8601 UTC timestamp of the most recent router reboot, or null

State values

StateDescription
monitoringNormal operation — periodically pinging targets
grace_periodOutage detected — waiting before power-cycling
rebootingRelay is open — router power is off
post_reboot_graceRouter recently rebooted — pinging periodically, waiting for internet to return
cooldownWaiting between retry attempts after a failed reboot
max_retries_exceededAll retries exhausted — stopped rebooting, requires manual intervention
disabledMonitoring is turned off

Notes


GET /watchdog/config

Returns all current configuration parameters with their active values.

Response 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"]
}

Fields

FieldTypeDefaultRangeDescription
enabledbooleantrueMaster monitoring switch
ping_interval_sinteger6030–300Seconds between ping rounds
fail_thresholdinteger32–10Consecutive failures before declaring outage
grace_before_reboot_sinteger1200–600Seconds to wait after outage before power cycle
power_off_duration_sinteger3010–120Seconds router stays powered off
post_reboot_grace_sinteger300120–900Max seconds to wait for internet after router reboot (exits early on success)
max_retriesinteger31–10Maximum power-cycle attempts before giving up
retry_cooldown_mininteger3010–120Minutes between retry attempts
scheduled_rebootstring | nullnull"HH:MM" or nullDaily preventative reboot time (24h format), or null to disable
ping_targetsstring[]["8.8.8.8", "1.1.1.1", "208.67.222.222"]1–4 IPv4 addressesTargets to ping each round. ALL must fail for a failure to count.

POST /watchdog/config

Update 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).

Request body

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
}

Validation rules

FieldRule
ping_interval_sInteger, 30–300
fail_thresholdInteger, 2–10
grace_before_reboot_sInteger, 0–600
power_off_duration_sInteger, 10–120
post_reboot_grace_sInteger, 120–900
max_retriesInteger, 1–10
retry_cooldown_minInteger, 10–120
scheduled_reboot"HH:MM" string (00:00–23:59), or null to disable
ping_targetsArray of 1–4 valid IPv4 address strings, or null to reset to defaults
enabledBoolean. Changing to true enters monitoring; false enters disabled and ensures relay is on.

Response 200 OK

Full config object (same as GET /watchdog/config).

Error responses

StatusBodyCondition
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

Side effects


POST /watchdog/reboot

Trigger 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.

Request body

None (ignored if present).

Response 200 OK

{
  "status": "rebooting"
}

Error responses

StatusBodyCondition
409"Already rebooting"Device is in rebooting or post_reboot_grace state

Side effects


GET /watchdog/history

Returns the event ring buffer — up to 16 most recent events, ordered oldest first.

Response 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
  }
]

Fields (per event)

FieldTypeDescription
eventstringEvent type (see table below)
timestamp_msintegerMilliseconds since device boot (not epoch time)
reboot_countintegerNumber of reboots at the time of this event

Event types

EventDescription
device_onlineDevice booted and started monitoring
outage_detectedAll ping targets failed for fail_threshold consecutive rounds
reboot_startedRouter power-cycle initiated (relay opened)
internet_restoredConnectivity recovered after an outage
max_retries_exceededAll retry attempts exhausted — watchdog stopped rebooting
scheduled_rebootDaily scheduled reboot triggered
manual_rebootUser-initiated reboot (via REST, MCP, MQTT, or button)
enabledMonitoring was enabled
disabledMonitoring was disabled

Notes


GET /watchdog/ping

Force an immediate connectivity check against all configured ping targets. Returns per-target results.

Response 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 }
  ]
}

Fields

FieldTypeDescription
internet_upbooleantrue if at least one target responded
targetsarrayPer-target results
targets[].ipstringIPv4 address that was pinged
targets[].successbooleanWhether this target responded
targets[].rtt_msintegerRound-trip time in milliseconds (0 if failed)

Side effects


POST /watchdog/stats/reset

Reset all cumulative statistics to zero. This clears lifetime counters and timestamps.

Request body

None (ignored if present).

Response 200 OK

{
  "status": "stats_reset"
}

What gets reset

StatReset to
total_reboots0
total_outages0
uptime_percent100.0 (monitoring time counters reset)
last_checknull
last_outagenull
last_rebootnull

Side effects


Common Notes

Content types

Error format

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.

Concurrency

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.

Timestamps

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).