tiger-sense-pulse

Sensor Pulse

Simulated school IoT sensors on Tiger Cloud (TimescaleDB): a hypertable, continuous aggregates and an anomaly query, tested offline.

tests · License: MIT · Built for MLH Global Hack Week: Hacktoberfest (Oct 2026)

Dashboard (GitHub Pages): aariv.me/tiger-sense-pulse/dashboard.html · Project summary: aariv.me/tiger-sense-pulse/docs/ · Code: https://github.com/draarivpatel-ui/tiger-sense-pulse

Live link: there is no hosted web app: this is a database project. It runs on your own Tiger Cloud service with ./run.sh, and dashboard.html is a static page it generates. The copy generated from the local test is the one served by GitHub Pages (links above).

One line: six simulated school sensors stream into a TimescaleDB hypertable on Tiger Cloud, continuous aggregates roll them up hourly and daily, and an anomaly query catches the two incidents hidden in the data.

Sensor Pulse dashboard: hourly average temperature for six sensors over 7 days, with the greenhouse and server-closet spikes, and the per-sensor summary from sensor_daily

The generated dashboard.html, rendered with headless Chromium (Oct 2026).

Contents: Judge it in 60 seconds · Features · Tech · Run it on Tiger Cloud · How it was tested · Limits · Parts 2-4 · AI assistance · Files

GHW challenges covered

Built by Aariv Patel for MLH Global Hack Week: Hacktoberfest (Oct 2026). Monitoring building and campus room telemetry (robotics lab, chemistry room, library, greenhouse, courtyard, and server closet) makes it possible to catch an AC or vent failure without scanning every raw reading by hand. This repository covers several Tiger Data challenges in one place (the first three below; vector search, Tiger CLI + MCP and real-time financial data are Parts 2-4 further down):

GHW challenge Where it lives
Set up a Tiger Cloud service and create your first Hypertable sql/01_schema.sql
Simulate an IoT sensor dataset with Tiger Cloud sql/02_simulate.sql
Accelerate Dashboards with Continuous Aggregates sql/03_continuous_aggregates.sql, dashboard.py
Ingest real-time financial data into Tiger Cloud (Part 4) finance/schema.sql, finance/ingest.py

Judge it in 60 seconds

No database, no pip install, no network: just git and Python 3.9+. Everything below was copied from real runs (Python 3.12, Oct 11-16, 2026).

1. Get the code and run the tests (~2 s):

git clone https://github.com/draarivpatel-ui/tiger-sense-pulse.git
cd tiger-sense-pulse
bash test.sh

Expected output (78 of the 82 ... ok lines trimmed; your time will differ):

test_empty_database_still_writes_a_page (test_dashboard.DashboardPage.test_empty_database_still_writes_a_page) ... ok
test_escapes_text_from_the_database (test_dashboard.DashboardPage.test_escapes_text_from_the_database) ... ok
...
test_strings_and_comments_are_terminated (test_sql_lint.SqlFileLint.test_strings_and_comments_are_terminated) ... ok
test_vector_schema (test_sql_lint.SqlFileLint.test_vector_schema) ... ok

----------------------------------------------------------------------
Ran 82 tests in 1.064s

OK

2. Replay the IoT simulation and the anomaly finder offline (~1 s). This uses the same Python replay of sql/02_simulate.sql and query 5 that the tests use, with the clock pinned and seeded noise so the output is repeatable:

cd tests && python3 -c "
from datetime import datetime, timezone
from test_generator import simulate, anomalies
data = simulate(datetime(2026, 10, 10, 23, tzinfo=timezone.utc), seed=1)
print('rows:', len(data))
for name, hour in anomalies(data):
    print('anomaly:', name, hour.strftime('%Y-%m-%d %H:00 UTC'))
"

Expected output:

rows: 12102
anomaly: greenhouse 2026-10-07 19:00 UTC
anomaly: greenhouse 2026-10-07 20:00 UTC
anomaly: server-rm 2026-10-09 17:00 UTC

12,102 rows and exactly the three injected hours (greenhouse vent shut for two, server-closet AC failure for one), no false positives. Compare with what the real TimescaleDB run returned for the same query in sample-output.txt (grep -A6 "^--- 5. Anomaly" sample-output.txt, run from the repo root):

--- 5. Anomaly finder: hours running > 4 C above that sensor's typical (median) for the same hour of day
    name    |         bucket         | max_temp | above_typical 
------------+------------------------+----------+---------------
 greenhouse | 2026-10-07 19:00:00+00 |     42.2 |           8.8
 greenhouse | 2026-10-07 20:00:00+00 |     42.3 |           9.0
 server-rm  | 2026-10-09 17:00:00+00 |     38.2 |          12.0
(3 rows)

3. See the dashboard: open dashboard.html in any browser (a static page generated from the continuous aggregates; preview at the top of this README).

The full pipeline on a live database is DATABASE_URL=... ./run.sh (see “Run it on Tiger Cloud” below); steps 1 and 2 check the files and the generator logic, not a live database.

Features

  1. Hypertable. sensor_data is created with WITH (tsdb.hypertable, tsdb.segmentby = 'sensor_id', tsdb.orderby = 'time DESC', tsdb.chunk_interval = '1 day'), so TimescaleDB splits it into one chunk per day. A normal sensors table holds metadata (name, type, location) and is joined in queries.
  2. IoT simulation. Six sensors around a school (robotics lab, chemistry lab, library, greenhouse, courtyard, server closet) report temperature, humidity and CPU load every 5 minutes for 7 days: 12,102 rows made with generate_series. Instead of plain random(), each sensor follows a daily sine curve (warmest around 3 PM US Central) plus noise, and two incidents are injected: a greenhouse vent left shut and a server-closet AC failure.
  3. Continuous aggregates. sensor_hourly pre-computes hourly avg/min/max temperature, average humidity, peak CPU and reading counts. sensor_daily is a hierarchical CAGG built on top of the hourly one. Both get refresh policies (every 30 min and every hour), so new data rolls up automatically in the background.
  4. Queries (sql/04_queries.sql): chunk count, latest reading per sensor (DISTINCT ON), 30-minute buckets with last(), a daily summary, an anomaly finder that flags any hour running more than 4 °C above that sensor’s typical (median) reading for the same hour of day, an EXPLAIN ANALYZE comparing the raw table with the CAGG, and the list of background refresh jobs.
  5. Dashboard (dashboard.py): reads only the continuous aggregates and writes a static dashboard.html with an inline-SVG chart, a per-sensor summary and the anomaly table. No JavaScript libraries.

Tech

Schema (as created by sql/01-03, and as it exists on the Tiger Cloud service)

Object Columns
sensors (table) id, name, type, location, base_temp, swing
sensor_data (hypertable) time, sensor_id, temperature, humidity, cpu
sensor_hourly (CAGG) bucket, sensor_id, avg_temp, min_temp, max_temp, avg_humidity, max_cpu, readings
sensor_daily (CAGG on sensor_hourly) day, sensor_id, avg_temp, min_temp, max_temp, readings

sql/04_queries.sql and dashboard.py use exactly these names (temperature, not temperature_c; day/avg_temp/min_temp/max_temp on sensor_daily).

Data sources

All data is simulated by sql/02_simulate.sql (sine-curve daily cycle plus random noise and two injected incidents). No real sensors or outside datasets are used. The one exception is Part 4 (finance/), which ingests live crypto prices from the CoinGecko Keyless Public API (data provided by CoinGecko, https://www.coingecko.com/en/api).

Results from a test run

From sample-output.txt (local test, see “How it was tested” below):

Run it on Tiger Cloud

  1. In Tiger Console, create a service and copy its connection string (postgres://tsdbadmin:...@<id>.tsdb.cloud.timescale.com:<port>/tsdb?sslmode=require).
  2. Either paste the four SQL files, in order, into the Console’s SQL editor (04_queries.sql uses \echo, which is psql-only; skip those lines in the web editor), or from a terminal with psql installed:

    export DATABASE_URL="postgres://tsdbadmin:<password>@<host>:<port>/tsdb?sslmode=require"
    ./run.sh
    
  3. Build the dashboard (dashboard.py reads DATABASE_URL too; if you used the Console editor in step 2, export it first, or the script stops with KeyError: 'DATABASE_URL'):

    export DATABASE_URL="postgres://tsdbadmin:<password>@<host>:<port>/tsdb?sslmode=require"   # skip if already set in step 2
    pip install -r requirements.txt
    python dashboard.py      # writes dashboard.html
    

01_schema.sql drops and recreates only the demo objects (sensors, sensor_data, sensor_hourly, sensor_daily), so it’s safe to re-run.

How it was tested

Offline test suite (no database needed)

bash test.sh     # same as: python3 -m unittest discover -s tests -v

82 tests, Python 3.9+ standard library only (no pip install, no database, no network). They also run on every push in GitHub Actions on Python 3.9, 3.12 and 3.13 (.github/workflows/tests.yml); I also ran the suite locally on 3.12 and 3.13 with DeprecationWarning and ResourceWarning turned into errors (82 OK on both).

The tests check the files and the generator logic, not a live database. The end-to-end test is still running the whole pipeline:

DATABASE_URL="postgres://..." ./run.sh     # builds schema, simulates data, creates CAGGs, runs the queries

Note: sample-output.txt shows times in UTC.

Limits and honesty

Part 2 (Oct 11): vector search with pgvector + pgvectorscale

vector/ adds AI-powered semantic search over staff maintenance notes stored right next to the sensor readings, on the same Tiger Cloud service.

Real output from Tiger Cloud is in vector/sample-output.txt. For example, “the air conditioning broke and the room overheated” returns the two server-closet AC notes from Oct 9 first (similarity 0.764 and 0.757), and the query plan shows the diskann index being used on each chunk.

Run it: pip install "psycopg[binary]" fastembed (quoted so zsh doesn’t treat the brackets as a glob) then DATABASE_URL=... python vector/vector_search.py --load

Part 3 (Oct 11): Tiger CLI + MCP with an AI agent

The Tiger CLI (v0.26.0) was installed, authenticated with tiger auth login --headless (device code approved in the Tiger Console), and tiger service list shows the mlh-hackweek service. mcp/mcp_client.py launches tiger mcp start over stdio and lets an AI agent (Hark, the assistant that built this repo) list the 19 Tiger MCP tools and call service_list and db_query against the live service. Transcript: mcp/sample-transcript.txt.

Part 4 (Oct 13): real-time financial data

finance/ ingests live crypto prices into the same Tiger Cloud service. The GHW guide uses Twelve Data, which needs an account and API key; this version uses the CoinGecko Keyless Public API instead (no key, no sign-up), so no new account was created.

Details and limits: finance/README.md. Data provided by CoinGecko (https://www.coingecko.com/en/api).

Built with AI assistance

Honest disclosure: this project was built with AI assistance. An AI assistant (Hark) wrote the SQL, the Python dashboard and this README and ran the local tests on behalf of Aariv Patel (GitHub: draarivpatel-ui) for his GHW Hacktoberfest participation. The schema and queries follow Tiger Data’s tutorials:

Files

sql/01_schema.sql                 sensors table + sensor_data hypertable
sql/02_simulate.sql               7 days of realistic simulated readings + 2 injected incidents
sql/03_continuous_aggregates.sql  hourly + hierarchical daily CAGGs with refresh policies
sql/04_queries.sql                demo / analysis queries
run.sh                            runs all four files against $DATABASE_URL
dashboard.py                      static HTML dashboard from the CAGGs
sample-output.txt                 output of ./run.sh from the local test
dashboard.html / dashboard-preview.png   generated dashboard from the local test
docs/hero.png                     screenshot of dashboard.html used at the top of this README
test.sh                           runs the offline test suite (tests/)
tests/                            SQL lint, generator, dashboard and README-consistency tests (standard library only)
vector/                           Part 2: pgvector + pgvectorscale semantic search over maintenance notes
mcp/                              Part 3: Tiger CLI + MCP client
finance/                          Part 4: real-time crypto prices -> hypertable + 1-minute OHLC continuous aggregate

License: MIT