mailwal

How I built an Urdu voice and WhatsApp reminder system for my father's medicines using Gemini 4 Argon

AHSYNC BYTES

If you live abroad while your parents are aging back home, you know that the hardest part isn't the flight back... It is the worry of them being healthy, and happy.

Last month, I went completely off social media. No blog posts, no instagram/facebook/youtube content, nothing...

That's because my father ("Abbu") got admitted into a hospital in Karachi for a week in a critical condition. He was discharged after a week and his recovery depended on a strict 7-step daily routine of meals, hydration, and post-meal medicines.

When I flew back to Karachi and while I was there, I could be with my father the entire day... ensuring he eats well, stays hydrated, and takes his medicines on time. Once I flew back to Stockholm, the entire daily routine sat on my mother's (Ammi's) shoulders.

As a Muslim, the Quranic dua from Surah Al-Isra (17:24) stays in your head... "Rabbi irhamhuma kama rabbayani saghira" (My Lord, have mercy upon them as they brought me up when I was small). You want to show up for them even from 5,000 km away, without turning every phone call home into an interrogation about whether Abbu took his afternoon dose.

So before heading back to Stockholm, I built Irhamhuma (irhamhuma.web.app)...and today I'm open-sourcing it as a self-hosted template for anyone in the same position.

Why normal phone alarms failed on day one

Every medication app I tried assumes the patient is holding the phone and eats breakfast at 08:00 sharp. At home in Karachi, three things broke that immediately:

  1. Passive alarms die in the kitchen. If an alarm rings while Ammi is cooking or Abbu says "give me 10 minutes", the alarm gets swiped away and forgotten. A reminder has to wait for an explicit confirmation, or follow up.
  2. Medicines are chained to meals, not clock times. Abbu's morning medicines have to be given 15 minutes after breakfast (+15m), and 2 boiled eggs with a milkshake 120 minutes after breakfast (+120m). If Abbu wakes up late and has breakfast at 12:30 instead of 09:00, a fixed 09:15 medicine alarm rings on an empty stomach.
  3. Sometimes a parent refuses, and the caregiver needs backup. When a recovering patient is tired and refuses a meal or a tablet, the caregiver shouldn't have to dial abroad and explain the situation from scratch.

How the 3-step cascade works

I'm a Google Developer Expert in Angular & AI, so I paired Gemini 4 Argon with the stack I can ship fastest on: Angular (standalone components + Signals) on Firebase Hosting, Cloud Functions (Gen 2), Cloud Scheduler ticking every 1 minute, Cloud Firestore, Google Cloud Text-to-Speech, Gemini 2.5 Flash, Twilio, and a
Telegram Bot.

Instead of calling Ammi's phone 7 times a day right away (which is noisy and costs $0.18/min on Twilio for Pakistani mobile numbers), every task runs through a Caregiver-First cascade:

  • Minute 0 (WhatsApp summary + Urdu voice note). Ammi gets a written Urdu message with the exact medicine names followed immediately by a native Urdu MP3 voice note. Why two messages? Because WhatsApp silently strips the text body whenever you attach an audio mediaUrl.. Lol. So the function sends the text card first and the .mp3 voice note a second later.
  • Replying 1 unlocks the meal chain. When Ammi replies 1 on WhatsApp (or presses 1 on a call), Firestore marks breakfast completed and calculates the exact timestamps for every WAITING_FOR_PARENT child task (morning_medicines at now + 15m, boiled_eggs_milkshake at now + 120m).
  • Minute 5 & Minute 10 (Voice call to Ammi). Look...a WhatsApp message is still easy to miss if the phone is in another room. If Ammi hasn't replied 1 after 5 minutes, Twilio calls her phone and plays the Urdu audio prompt. She can press 1 (done), 2 (snooze 15m), or 3 (Abbu is refusing).
  • Minute 11 (Escalation + Press-9 live bridge to Stockholm). Only if Ammi misses two calls (or presses 3), the system alerts me in Stockholm and rings my phone. If I press 9, Twilio's <Dial> bridges my call straight to home so I can talk to Abbu directly.

I also ended up building a simulation right into the platform so I can test it while building it. I can listen to the audio voice note, see the text message, send the messages on WhatsApp or try out the actual Twilio call, and simulate the events as well. This helped me test the flow thoroughly before asking Ammi to try the platform out.

The bugs that only show up in real family use

The code you write at your desk is never the code that survives a week with your parents. Yep. And that's why even after testing the flows, three bugs forced the best architectural decisions in this repo:

1. Twilio's <Say> cannot speak Pakistani Urdu

Twilio's built-in <Say> verb doesn't support ur-PK. If you feed Roman Urdu into an English TTS voice, it phoneticizes the words so badly that nobody at home can tell which medicine it's asking for.

So /twimlRouter/audio.mp3 bypasses Twilio TTS completely. It takes the Nastaliq Urdu script (urduScriptNative, generated by Gemini 2.5 Flash with Zod structured outputs when a schedule is created), rewrites button instructions depending on the channel ("1 likh kar bhejein" for WhatsApp vs "1 dabayein" for a phone call), synthesizes it at 0.90x speed using Google Cloud Text-to-Speech (ur-PK-Standard-A / ur-IN-Wavenet-A), and streams raw audio/mpeg bytes to Twilio <Play> and WhatsApp.

2. Meta's 24-hour window (Error 63016) vs a $0 Telegram bot

I wanted a short text message on my phone whenever Ammi marked a task done, just so I'd know Abbu took his morning medicines without opening the dashboard.

It worked for one day, and then completely stopped delivering to my Swedish number.

When I checked the Twilio logs, every completion ping to my +46 number was failing with errCode: 63016 (status: undelivered). Because Ammi replies 1 on WhatsApp several times a day, Meta's 24-hour customer service window for her +92 number resets constantly. On my phone, I was only reading the updates without replying...so 24 hours after my last message to the bot, Meta blocked freeform messages to me.

Opening that window with Meta Utility Templates to Sweden (Rest of Western Europe) costs $0.0221 per message (~$4.64/month for 7 tasks a day). Instead, we split the channels per role:

  • Caregiver channel (WHATSAPP): Ammi stays on WhatsApp in Pakistan, where Telegram is restricted by ISPs.
  • Escalation channel (TELEGRAM): My completion pings and escalation buttons go through a Telegram Bot ($0.00/month, no 24-hour window rule). When you paste your @BotFather token into the Settings UI, the Cloud Function calls Telegram's setWebhook automatically, and sending /start to the bot hands you back your Chat ID.

For open-source users in countries where Telegram isn't blocked, you can set both Caregiver and Escalation to TELEGRAM and run the entire messaging and voice-note loop for $0/month.

3. The security holes you have to close before open-sourcing

Originally this was a private family tool. Before making the repo public, I ran an audit on what would happen if someone cloned it or found the public Firebase bundle:

  1. Multi-device startup overwrite. Initially, opening the Angular app on a new phone with empty localStorage pushed default template tasks to Firestore on startup...overwriting custom schedules. Now startup strictly runs syncStateToCloud({ pullOnly: true }) and hydrates from Firestore first.
  2. Toll-fraud protection on public webhooks. Cloud Functions URLs are public. Leaving /trigger-live-call or TwiML webhooks unauthenticated on a Twilio account with $0.18/min Pakistan permissions is a toll-fraud bill waiting to happen. Every Twilio route now enforces cryptographic X-Twilio-Signature validation (twilio.validateRequest), dashboard routes require a constant-time crypto.timingSafeEqual passcode header (x-irhamhuma-key), and outbound calls enforce an E.164 country prefix allowlist (ALLOWED_CALL_PREFIXES=+92,+46).

Honest caveats before you deploy it

It is a family reminder and escalation system, not a medical device, and it does not replace a caregiver in the room.

Also, be realistic about telephony costs if your parents are in Pakistan. While Telegram is $0 and WhatsApp messages inside the 24-hour reply window cost $0.005 each (~$3/month), Twilio charges $0.18/min for outbound cellular calls to Pakistani mobile numbers. With the WhatsApp-first cascade catching ~75-85% of tasks at Minute 0, a single family in Pakistan runs around $15 to $26/month on Twilio (compared to ~$50/month if every reminder were a phone call). In India ($0.0496/min) or on a pure Telegram setup, it's a fraction of that.

Try it or fork it for your parents

The repository is scrubbed of all personal phone numbers and medicine names and ships as a self-hosted template under the MIT license. It supports Urdu (ur-PK, ur-IN), Hindi (hi-IN), Arabic (ar-XA), Bengali (bn-IN), and English (en-US) voices out of the box.

git clone https://github.com/AhsanAyaz/irhamhuma.git
cd irhamhuma
cp .firebaserc.example .firebaserc
cp .env.example functions/.env

Repo: https://github.com/AhsanAyaz/irhamhuma

Architecture & setup guide: https://github.com/AhsanAyaz/irhamhuma/blob/master/docs/ARCHITECTURE_AND_PLAN.md

Disclosure: As a Google Developer Expert in Angular & AI, I receive Google Cloud credits that cover the Firebase Functions, Firestore, Cloud Scheduler, Cloud TTS, and Gemini API usage on my deployment. The Twilio bill is out of pocket.

If you have aging parents back home, I hope this saves your family a few missed doses and a few stressful phone calls. Please remember my father and mother in your duas (prayers)

:) Ahsan

Powered by Ghost