cloudflare-uhoo-poller

star 0

Use when implementing or debugging the uHoo continuous monitoring poller running on Cloudflare Workers with Supabase

jefflyt By jefflyt schedule Updated 6/7/2026

name: cloudflare-uhoo-poller description: Use when implementing or debugging the uHoo continuous monitoring poller running on Cloudflare Workers with Supabase

Cloudflare uHoo Poller

Overview

A Cloudflare Worker that polls the uHoo API every minute and stores readings in Supabase. Replaced the previous Supabase Edge Function + pg_cron approach due to cold start latency (5-10 min gaps).

Why Cloudflare Workers: 10ms cold starts, exact 60s intervals, free tier, completely independent of the FastAPI app.

Core principle: Poll all active devices, deduplicate by timestamp, store CmReading + CmFinding rows.

Architecture

Cloudflare Cron Trigger (every 60s)
    ↓
Worker fetches devices from Supabase (device_connection)
    ↓
For each device (in parallel):
  ├─ Fetch latest reading from uHoo API
  ├─ Dedup check: skip if timestamp already in cm_reading
  ├─ Insert cm_reading rows (with tenant_id string)
  ├─ Evaluate thresholds → insert cm_finding rows
  └─ Update last_poll_at

Quick Reference

File Purpose
poller/src/index.ts Worker entry point + polling logic
poller/src/uhoo.ts uHoo API client (token mgmt + parsing)
poller/src/thresholds.ts Threshold evaluation (GOOD/WATCH/CRITICAL)
poller/wrangler.toml Cloudflare config + cron trigger
poller/package.json Dependencies (just @supabase/supabase-js)

Key Patterns

Deduplication by Timestamp

The uHoo API returns the latest reading only. If the cron fires but the device hasn't produced a new reading since the last poll, skip the insert:

// Check if this exact timestamp already exists
const { count } = await supabase
  .from('cm_reading')
  .select('*', { count: 'exact', head: true })
  .eq('device_id', device.id)
  .eq('reading_timestamp', readingTs.toISOString())

if (count > 0) return // Already stored, skip

tenant_id Must Be String

The cm_reading table is partitioned by month. The tenant_id column must be a string, not UUID:

// Site returns UUID, must convert to string
const tenantId = String(site.tenant_id)

Parallel Device Polling

Poll all devices in parallel to stay within the 30s Worker timeout:

const results = await Promise.allSettled(
  devices.map(d => pollDevice(d, tenantMap))
)

Common Mistakes

Mistake Fix
Using limit=1 and missing data Fetch all devices in parallel, each gets its own reading
Passing UUID to tenant_id Convert: String(site.tenant_id)
No dedup → duplicate rows Check timestamp before insert
Blocking on sequential polls Use Promise.allSettled for parallel
Forgetting to update last_poll_at Always update on success or error

Deployment

cd poller
npm install
npx wrangler login
npx wrangler deploy
# Set secrets
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_KEY
npx wrangler secret put UHOO_CLIENT_CODE

Debugging

# Check cron run logs
npx wrangler tail

# Verify cron is scheduled
npx wrangler deploy --dry-run

# Check Supabase for recent readings
curl -H "apikey: $SERVICE_KEY" \
  "https://$PROJECT.supabase.co/rest/v1/cm_reading?order=reading_timestamp.desc&limit=5"
Install via CLI
npx skills add https://github.com/jefflyt/fjsafespace01 --skill cloudflare-uhoo-poller
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator