Inspiration

every week, i’d wake up sweaty.

in my dorm, we pay for our own aircon. i’ve always been lazy about checking the account balance. the only signal i get that it ran out of money is when the aircon stops working.

so yeah... i wake up sweaty.

i hate waking up sweaty.

with some guidance and a bit of copying network requests, i managed to replicate the aircon website’s functionality inside telegram. now i can: • check my balance • predict when i’m running out • top up before my room turns into a sauna

What it does

A Telegram bot that monitors your NUS aircon credits (EVS2 Consumer Portal) so you never wake up sweaty again. Instead of manually checking the portal, you can:

  • Check your balance instantly via Telegram
  • Track daily usage with breakdowns and averages
  • Predict when you'll run out based on your usage pattern
  • Get smart alerts when balance drops below $3, below $1, or < 2 days remain
  • Receive daily summaries at 10am SGT with your usage recap
  • Top up before it's too late — direct link to the portal Supported residences: RVRC, Acacia College, Pioneer House, and any venue using the cp2evs system. ## How we built it

The Flutter web app at cp2nus.evs.com.sg makes API calls to ore.evs.com.sg. We reverse-engineered the network requests and called the same backend endpoints directly. Key technical decisions:

  • Node.js + TypeScript + Telegraf — lightweight, type-safe, no framework bloat
  • Native fetch — no axios or extra dependencies
  • Bearer token auth — same pattern as the Flutter app (login returns JWT, subsequent requests use Authorization: Bearer)
  • Permission workaround — the backend has two permission levels: read (looser) and restrictive (stricter). We discovered get_month_to_date_usage uses read permissions, bypassing the 403 errors we hit on the daily history endpoints.

Architecture:

  • evsClient.ts — API client handling auth + all endpoint wrappers
  • bot.ts — Telegram command handlers, daily reminder scheduler, conversational onboarding
  • storage.ts — Encrypted credential storage (in-memory or persistent)
  • config.ts — Environment-based configuration

Challenges we ran into

  1. Permission denied (403) — The backend's meter_p_reading_daily and usage rank endpoints require list operation permissions that our accounts don't have. We found alternative endpoints that use read permissions instead.
  2. Flutter is a pain -- Had to look at the network tab multiple times to reverse engineer the APIs
  3. Multiple hostels use different endpoints - had to have custom behavior to ensure that it worked for different hostels
  4. kWh vs money accounts Some NUS residences use kWh-credit instead of dollar-based billing. Added fallback handling to detect and support both account types.
  5. Legacy accounts Some older accounts get 403 on certain endpoints. Added fallback to legacy portal data instead of failing completely.

Accomplishments that we're proud of

  • Reverse-engineered a production API without documentation
  • Conversational onboarding — new users get guided through credential entry step-by-step with security reassurances
  • 2-tier reminder system — smart alerts for threshold warnings + daily summaries for routine check-ins

What we learned

Having a product that solves a real problem beats a project that has no real users. I got DMs from people using my bot asking for help for onboarding,

What's next for NUS Aircon Checker

  • Spending analytics — Weekly/monthly trends, usage graphs, cost projections
  • Smart predictions — Factor in weekends vs weekdays (usage patterns differ significantly)
  • Scheduled top-ups — Integrate with the portal to auto-top-up when balance is low (if the API allows it)

Built With

Share this project:

Updates