Skip to content

feat(community): add prayer-times ability#217

Merged
Rizwan-095 merged 5 commits intoopenhome-dev:devfrom
mahsumaktas:community/prayer-times
Mar 18, 2026
Merged

feat(community): add prayer-times ability#217
Rizwan-095 merged 5 commits intoopenhome-dev:devfrom
mahsumaktas:community/prayer-times

Conversation

@mahsumaktas
Copy link
Copy Markdown
Contributor

Note: This is a re-submission of #216 after addressing review feedback from @uzair401.
Original PR was closed by the reviewer — reopening was not possible without write access.

Summary

Voice-activated Islamic prayer times assistant with automatic background reminders.

  • Skill: "When is the next prayer?", "What are today's prayer times?", specific prayer queries, location setup, calculation method selection
  • Background Daemon: 5-minute advance reminder before each prayer + notification at prayer time
  • API: Aladhan Prayer Times API — free, no API key required
  • Calculation methods: ISNA, MWL, Diyanet (Turkey), Karachi, Makkah, Egypt

Changes Since #216

Fixed both issues raised by @uzair401:

  1. ✅ Removed resume_normal_flow() from the daemon loop — daemons run indefinitely, this call is incorrect there
  2. ✅ Fixed send_interrupt_signal(): now awaited with no arguments, followed by speak() for the actual message; _safe_interrupt is now async

Verified against the Background and Alarm templates.

Mahsum and others added 3 commits March 16, 2026 21:14
Skill + Background Daemon combo for Islamic prayer times:
- Voice queries: next prayer, all times, specific prayer lookup
- Background reminders: 5-min advance + adhan notification
- Location-based via Aladhan API (free, no key required)
- Multiple calculation methods (ISNA, MWL, Diyanet, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove resume_normal_flow() from daemon loop (daemons run indefinitely)
- Fix send_interrupt_signal(): no arguments, must be awaited
- Use speak() separately after interrupt signal
- Make _safe_interrupt async with proper await chain

Addresses feedback from @uzair401 on PR openhome-dev#216
@mahsumaktas mahsumaktas requested a review from a team as a code owner March 17, 2026 06:55
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 17, 2026

✅ Community PR Path Check — Passed

All changed files are inside the community/ folder. Looks good!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 17, 2026

🔀 Branch Merge Check

PR direction: community/prayer-timesdev

Passedcommunity/prayer-timesdev is a valid merge direction

@github-actions github-actions bot added the community-ability Community-contributed ability label Mar 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 17, 2026

✅ Ability Validation Passed

📋 Validating: community/prayer-times
  ✅ All checks passed!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 17, 2026

🔍 Lint Results

__init__.py — Empty as expected

Files linted: community/prayer-times/background.py community/prayer-times/main.py

✅ Flake8 — Passed

✅ All checks passed!

@uzair401
Copy link
Copy Markdown
Contributor

Hello @mahsumaktas, Thanks for the submission again, here are a few suggestions to implement to make the conversation feel more natural for the user:

  1. SYSTEM_PROMPT has no instruction for spoken output quality — the "unknown" intent path falls through to _speak_next_prayer silently, and if the LLM ever returns a QUESTION: style response or markdown, it would go straight into speak(). Add to the prompt: "Respond ONLY with valid JSON. No extra text, no markdown, no bullet points, no questions."

  2. SYSTEM_PROMPT intent examples read like API documentation, not how a US speaker actually talks. Replace the inline examples with natural phrasing guidance — "what time is fajr" instead of "Query a specific prayer", "what are my times today" instead of "Query all today's prayer times", "I'm in Chicago, Illinois" instead of "user mentions a city/country".

  3. _format_all_times produces a comma-joined string of all 6 prayers read as one run-on sentence — roughly 40–50 words depending on city. "Today's prayer times for {data['city']}: {formatted}" would read as "Today's prayer times for Chicago: Fajr: 5:12, Sunrise: 6:44, Dhuhr: 12:30..." — far too long and hard to follow on a speaker with no screen. Split into two speak() calls: the first three prayers, pause, then the last three. Or offer "Which prayer do you want?" as a follow-up instead of reading all six unprompted.

  4. f"Calculation method updated to {method}." — speaks the raw method number (e.g. "Calculation method updated to 2") which means nothing to the user. Map the number to a name before speaking: {2: "ISNA", 3="MWL", 4="Makkah", ...} and say "Calculation method set to ISNA."

  5. "I need your location to get accurate prayer times. What city and country are you in?" in _setup — 18 words, slightly over target for a standard prompt. Tighten to: "What city and country are you in?" (7 words — the reason is obvious from context).

  6. f"Location set to {city}, {country}. You can change this anytime." — second sentence adds no value on a voice device (there's no visible menu to change it from). Drop it: "Got it, location set to {city}."

  7. "Sorry, I couldn't determine your location. Please try again." in _setup — 10 words, fine for length, but offers no path forward. The user doesn't know what to say differently. Change to: "Didn't catch that — try saying your city and country, like 'Dallas, United States'."

  8. f"I couldn't find the time for {prayer}. Available prayers are: {', '.join(PRAYER_NAMES)}." — reading a comma-joined list of 6 prayer names aloud is awkward TTS and over 20 words. Change to: "I don't have a time for {prayer}. Try asking for Fajr, Dhuhr, or another prayer by name."

  9. "All prayers for today have passed. Fajr will be the next prayer tomorrow." — 14 words, within limit, but "Fajr will be the next prayer tomorrow" is a slightly stilted construction. Change to: "All done for today. Fajr is next, tomorrow morning." (9 words, more natural).

  10. "Sorry, something went wrong. Please try again." appears in both files — acceptable length but gives no path forward on a voice device. Change to: "Something went wrong. Try asking again."

  11. f"{name} is in {mins} minute{'s' if mins != 1 else ''}. Time to prepare." in the background daemon — the second sentence "Time to prepare." is abrupt and slightly robotic. Change to: "{name} in {mins} minute{'s' if mins != 1 else ''} — time to get ready." Same length, more natural spoken cadence.

  12. The _setup flow uses user_response() instead of run_io_loop() — on a smart speaker user_response() typically doesn't re-prompt if the user says nothing or the STT returns empty. Since location setup is a required gate, use run_io_loop("What city and country are you in?") so the device re-asks if it gets no response.

  13. No hardcoded string matching issues found — intent routing correctly goes through the LLM classifier throughout. No exit/confirmation word lists exist in this ability. No menu-driven sequential prompts found. These areas are clean.

- SYSTEM_PROMPT: add 'Respond ONLY with valid JSON' instruction, replace
  API-style examples with natural spoken phrasing
- Split all_times into two speak() calls (3+3) to avoid run-on sentence
- Map method number to name before speaking (e.g. 'ISNA' not '2')
- Shorten setup prompt: use run_io_loop() instead of speak+user_response
- Drop 'You can change this anytime' (no visible menu on voice device)
- Improve error messages with actionable hints
- Naturalize daemon reminder text
- Tighten all spoken strings for voice-first delivery

Addresses review from @uzair401 on PR openhome-dev#217
@mahsumaktas
Copy link
Copy Markdown
Contributor Author

Thanks @uzair401 for the thorough voice UX review! All 12 suggestions implemented:

  1. SYSTEM_PROMPT: Added 'Respond ONLY with valid JSON' — prevents markdown/questions leaking into speak()
  2. Natural intent examples: Replaced API-style docs with spoken phrasing ('what time is fajr' instead of 'Query a specific prayer')
  3. Split all_times: Now two speak() calls (Fajr/Sunrise/Dhuhr, then Asr/Maghrib/Isha) — no more 50-word run-on
  4. Method name mapping: 'Calculation method set to ISNA' instead of '...to 2'
  5. Setup prompt: Shortened to 'What city and country are you in?' using run_io_loop() for re-prompting
  6. Dropped: 'You can change this anytime' — no visible menu on voice device
  7. Location error: Added example hint ('try saying Dallas, United States')
  8. Specific prayer error: Shortened, suggests asking by name
  9. All passed message: 'All done for today. Fajr is next, tomorrow morning.'
  10. Generic error: 'Something went wrong. Try asking again.'
  11. Daemon reminder: '{name} in {mins} minutes — time to get ready.'
  12. Setup uses run_io_loop(): Re-asks if STT returns empty

Copy link
Copy Markdown
Contributor

@uzair401 uzair401 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved for community.
We will proceed with adopting this ability for a 30-day evaluation period to monitor performance, identify any bugs, and implement any necessary fixes or optimizations. If the ability proves stable and meets our quality standards during this period, we will move forward with publishing it to the marketplace, ensuring full credit is given to the original author for their work and contribution.
We truly appreciate your effort and encourage you to continue developing and submitting new abilities. Contributions like yours help strengthen the ecosystem, and we look forward to seeing more of your work.

@Rizwan-095 Rizwan-095 merged commit a704959 into openhome-dev:dev Mar 18, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-ability Community-contributed ability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants