MUIS Prayer Times 2025

Hi All,

I would like to share a RESTful Sensor project to import data from the MUIS (Majlis Ugama Islam Singapura) API for accurate Prayer Times for Singapore. The code below provides individual sensors for Subuh, Syuruk, Zohor, Asar, Maghrib & Isyak Prayer Times. These sensors can be used for Azan automations in Node Red (Time Node) as a trigger.

The sensors will update hourly and if the Prayer Time for Today has passed, it will show the following day’s Prayer Time instead.

e.g. If time now is 1650h and Asar Prayer Time for today was at 1620h, tomorrow’s Asar Prayer Time will be shown instead.

INSTALLATION:
Add the codes below to your configurations.yaml file.

A full restart is required if this is the first time you have added the “sensor” or “template” component to your Home Assistant setup.

UPDATE: Updated the code to be usable with both Node-RED and Home Assistant’s native GUI Automation with the Template Sensor below.

Code to retrieve MUIS prayer times:
Prayer times will be in ISO 8601 format (Only Node-RED Automations).
E.g. 2025-04-26T16:21:00

Sensor:
  - platform: rest
    name: "Subuh ISO"
    resource: "https://data.gov.sg/api/action/datastore_search?resource_id=d_e81ea2337599b674c4f645c1af93e0dc&limit=365&fields=Date,Subuh"
    value_template: >
      {% set today = now().strftime('%Y-%m-%d') %}
      {% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%d') %}
      {% set records = value_json.result.records %}
      {% set today_row = records | selectattr("Date", "equalto", today) | list %}
      {% set tomorrow_row = records | selectattr("Date", "equalto", tomorrow) | list %}
      {% if today_row and tomorrow_row %}
        {% set t_hour, t_minute = today_row[0].Subuh.split(':') %}
        {% set now_total = now().hour * 60 + now().minute %}
        {% set prayer_total = (t_hour | int) * 60 + (t_minute | int) %}
        {% if now_total < prayer_total %}
          {{ today }}T{{ '%02d' % (t_hour | int) }}:{{ '%02d' % (t_minute | int) }}:00
        {% else %}
          {% set th, tm = tomorrow_row[0].Subuh.split(':') %}
          {{ tomorrow }}T{{ '%02d' % (th | int) }}:{{ '%02d' % (tm | int) }}:00
        {% endif %}
      {% else %}
        unknown
      {% endif %}
    scan_interval: 3600
    headers:
      Accept: application/json

  - platform: rest
    name: "Zohor ISO"
    resource: "https://data.gov.sg/api/action/datastore_search?resource_id=d_e81ea2337599b674c4f645c1af93e0dc&limit=365&fields=Date,Zohor"
    value_template: >
      {% set today = now().strftime('%Y-%m-%d') %}
      {% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%d') %}
      {% set records = value_json.result.records %}
      {% set today_row = records | selectattr("Date", "equalto", today) | list %}
      {% set tomorrow_row = records | selectattr("Date", "equalto", tomorrow) | list %}
      {% if today_row and tomorrow_row %}
        {% set t_hour, t_minute = today_row[0].Zohor.split(':') %}
        {% set t_hour = t_hour | int + 12 %}
        {% set now_total = now().hour * 60 + now().minute %}
        {% set prayer_total = t_hour * 60 + (t_minute | int) %}
        {% if now_total < prayer_total %}
          {{ today }}T{{ '%02d' % t_hour }}:{{ '%02d' % (t_minute | int) }}:00
        {% else %}
          {% set th, tm = tomorrow_row[0].Zohor.split(':') %}
          {% set th = th | int + 12 %}
          {{ tomorrow }}T{{ '%02d' % th }}:{{ '%02d' % (tm | int) }}:00
        {% endif %}
      {% else %}
        unknown
      {% endif %}
    scan_interval: 3600
    headers:
      Accept: application/json

  - platform: rest
    name: "Asar ISO"
    resource: "https://data.gov.sg/api/action/datastore_search?resource_id=d_e81ea2337599b674c4f645c1af93e0dc&limit=365&fields=Date,Asar"
    value_template: >
      {% set today = now().strftime('%Y-%m-%d') %}
      {% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%d') %}
      {% set records = value_json.result.records %}
      {% set today_row = records | selectattr("Date", "equalto", today) | list %}
      {% set tomorrow_row = records | selectattr("Date", "equalto", tomorrow) | list %}
      {% if today_row and tomorrow_row %}
        {% set t_hour, t_minute = today_row[0].Asar.split(':') %}
        {% set t_hour = t_hour | int + 12 %}
        {% set now_total = now().hour * 60 + now().minute %}
        {% set prayer_total = t_hour * 60 + (t_minute | int) %}
        {% if now_total < prayer_total %}
          {{ today }}T{{ '%02d' % t_hour }}:{{ '%02d' % (t_minute | int) }}:00
        {% else %}
          {% set th, tm = tomorrow_row[0].Asar.split(':') %}
          {% set th = th | int + 12 %}
          {{ tomorrow }}T{{ '%02d' % th }}:{{ '%02d' % (tm | int) }}:00
        {% endif %}
      {% else %}
        unknown
      {% endif %}
    scan_interval: 3600
    headers:
      Accept: application/json

  - platform: rest
    name: "Maghrib ISO"
    resource: "https://data.gov.sg/api/action/datastore_search?resource_id=d_e81ea2337599b674c4f645c1af93e0dc&limit=365&fields=Date,Maghrib"
    value_template: >
      {% set today = now().strftime('%Y-%m-%d') %}
      {% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%d') %}
      {% set records = value_json.result.records %}
      {% set today_row = records | selectattr("Date", "equalto", today) | list %}
      {% set tomorrow_row = records | selectattr("Date", "equalto", tomorrow) | list %}
      {% if today_row and tomorrow_row %}
        {% set t_hour, t_minute = today_row[0].Maghrib.split(':') %}
        {% set t_hour = t_hour | int + 12 %}
        {% set now_total = now().hour * 60 + now().minute %}
        {% set prayer_total = t_hour * 60 + (t_minute | int) %}
        {% if now_total < prayer_total %}
          {{ today }}T{{ '%02d' % t_hour }}:{{ '%02d' % (t_minute | int) }}:00
        {% else %}
          {% set th, tm = tomorrow_row[0].Maghrib.split(':') %}
          {% set th = th | int + 12 %}
          {{ tomorrow }}T{{ '%02d' % th }}:{{ '%02d' % (tm | int) }}:00
        {% endif %}
      {% else %}
        unknown
      {% endif %}
    scan_interval: 3600
    headers:
      Accept: application/json

  - platform: rest
    name: "Isyak ISO"
    resource: "https://data.gov.sg/api/action/datastore_search?resource_id=d_e81ea2337599b674c4f645c1af93e0dc&limit=365&fields=Date,Isyak"
    value_template: >
      {% set today = now().strftime('%Y-%m-%d') %}
      {% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%d') %}
      {% set records = value_json.result.records %}
      {% set today_row = records | selectattr("Date", "equalto", today) | list %}
      {% set tomorrow_row = records | selectattr("Date", "equalto", tomorrow) | list %}
      {% if today_row and tomorrow_row %}
        {% set t_hour, t_minute = today_row[0].Isyak.split(':') %}
        {% set t_hour = t_hour | int + 12 %}
        {% set now_total = now().hour * 60 + now().minute %}
        {% set prayer_total = t_hour * 60 + (t_minute | int) %}
        {% if now_total < prayer_total %}
          {{ today }}T{{ '%02d' % t_hour }}:{{ '%02d' % (t_minute | int) }}:00
        {% else %}
          {% set th, tm = tomorrow_row[0].Isyak.split(':') %}
          {% set th = th | int + 12 %}
          {{ tomorrow }}T{{ '%02d' % th }}:{{ '%02d' % (tm | int) }}:00
        {% endif %}
      {% else %}
        unknown
      {% endif %}
    scan_interval: 3600
    headers:
      Accept: application/json

Template Sensors Code:
Device class set to timestamp to enable for use with HA GUI Automations and with Node-RED. It also displays state in a more visually pleasing format. E.g. 26 April 2025 at 16:21

template:
  - sensor:
      - name: "MUIS - Subuh"
        device_class: timestamp
        state: >
          {% set iso = states('sensor.subuh_iso') %}
          {% if iso not in ['unknown', 'unavailable', None] %}
            {{ as_datetime(iso ~ '+08:00').isoformat() }}
          {% else %}
            unknown
          {% endif %}

  - sensor:
      - name: "MUIS - Zohor"
        device_class: timestamp
        state: >
          {% set iso = states('sensor.zohor_iso') %}
          {% if iso not in ['unknown', 'unavailable', None] %}
            {{ as_datetime(iso ~ '+08:00').isoformat() }}
          {% else %}
            unknown
          {% endif %}

  - sensor:
      - name: "MUIS - Asar"
        device_class: timestamp
        state: >
          {% set iso = states('sensor.asar_iso') %}
          {% if iso not in ['unknown', 'unavailable', None] %}
            {{ as_datetime(iso ~ '+08:00').isoformat() }}
          {% else %}
            unknown
          {% endif %}

  - sensor:
      - name: "MUIS - Maghrib"
        device_class: timestamp
        state: >
          {% set iso = states('sensor.maghrib_iso') %}
          {% if iso not in ['unknown', 'unavailable', None] %}
            {{ as_datetime(iso ~ '+08:00').isoformat() }}
          {% else %}
            unknown
          {% endif %}

  - sensor:
      - name: "MUIS - Isyak"
        device_class: timestamp
        state: >
          {% set iso = states('sensor.isyak_iso') %}
          {% if iso not in ['unknown', 'unavailable', None] %}
            {{ as_datetime(iso ~ '+08:00').isoformat() }}
          {% else %}
            unknown
          {% endif %}

Please note that this will only be valid for Singapore Prayer Times (by MUIS) in 2025 and that the resource URL will need to be updated to for the sensors to be functional after 2025. Barring changes in the API structure, this should continue to be usable with a simple update to the relevant year’s Prayer Timetable API URL.

1 Like

Thank you brother for sharing.
Here is mine. using Singapore MUIS Prayer Time-Table. [wrap=“right”]

  • platform: template
    sensors:
    prayer_fajr:
    friendly_name: “Fajr Prayer”
    value_template: >
    {% set today = now().strftime(‘%Y-%m-%d’) %}
    {% set ns = namespace(found=false, time=‘05:30’) %}
    {% set csv_data = [
    “date,fajr,sunrise,dhuhr,asr,maghrib,isha”,
    “2026-01-06,05:47,07:10,13:12,16:36,19:13,20:27”,
    “2026-01-07,05:47,07:10,13:13,16:37,19:13,20:28”,
    “2026-01-08,05:48,07:11,13:13,16:37,19:14,20:28”,
    “2026-01-09,05:48,07:11,13:14,16:37,19:14,20:28”,
    “2026-01-10,05:49,07:11,13:14,16:38,19:15,20:29”,
    “2026-01-11,05:49,07:12,13:15,16:38,19:15,20:29”,
    “2026-01-12,05:50,07:12,13:15,16:38,19:15,20:29”,
    “2026-01-13,05:50,07:13,13:15,16:39,19:16,20:30”,
    “2026-01-14,05:51,07:13,13:16,16:39,19:16,20:30”,
    “2026-01-15,05:51,07:13,13:16,16:39,19:17,20:30”,
    “2026-01-16,05:51,07:14,13:16,16:39,19:17,20:30”, continue till end of the year.
    prayer_dhuhr:
    friendly_name: “Dhuhr Prayer”
    value_template: >
    {% set today = now().strftime(‘%Y-%m-%d’) %}
    {% set ns = namespace(found=false, time=‘12:15’) %}
    {% set csv_data = [
    “date,fajr,sunrise,dhuhr,asr,maghrib,isha”,
    “2025-12-31,05:43,07:06,13:09,16:33,19:09,20:24”,
    “2026-01-01,05:44,07:08,13:10,16:34,19:11,20:25”,
    “2026-01-02,05:45,07:08,13:11,16:35,19:11,20:26”,
    “2026-01-03,05:45,07:09,13:11,16:35,19:12,20:26”,
    “2026-01-04,05:46,07:09,13:12,16:35,19:12,20:27”,
    “2026-01-05,05:46,07:09,13:12,16:36,19:12,20:27”,
    “2026-01-06,05:47,07:10,13:12,16:36,19:13,20:27”,
    “2026-01-07,05:47,07:10,13:13,16:37,19:13,20:28”,
    “2026-01-08,05:48,07:11,13:13,16:37,19:14,20:28”, …continue

hera is the automation:

  • id: ‘1757772532067’
    alias: 1. Azan Subuh (Fajr) - 5 min early
    description: Fajr prayer time automation - starts 5 minutes before
    triggers:
    • trigger: template
      value_template: "{% set prayer_time = states(‘sensor.prayer_fajr’) %} {% if prayer_time
      not in [‘unknown’, ‘unavailable’] and ‘:’ in prayer_time %}\n {% set prayer_minutes
      = prayer_time.split(‘:’)[0]|int * 60 + prayer_time.split(‘:’)[1]|int %}\n {%
      set trigger_minutes = prayer_minutes - 5 %}\n {% set current_minutes = now().hour
      • 60 + now().minute %}\n {{ current_minutes == trigger_minutes }}\n{% else
        %}\n false\n{% endif %}"
        conditions:
        actions:
    • variables:
      prayer_names:
      fajr: Subuh
      dhuhr: Zohor
      asr: Asar
      maghrib: Maghrib
      isha: Ishak
      current_prayer: fajr
    • action: tts.google_translate_say
      data:
      message: Waktu solat {{ prayer_names[current_prayer] }} dalam 5 menit lagi
      language: ms
      entity_id: media_player.ajaxhanna_speaker
    • delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
    • action: light.turn_on
      target:
      entity_id:
      • light.dining_wled_main
      • light.air_con_wled
      • light.dining_wled_2
        data: {}
    • action: remote.send_command
      metadata: {}
      data:
      device: Radio Warna
      command: toggle
      target:
      entity_id: remote.dining_room_broadlink
    • action: timer.start
      data:
      duration: 00:10:00
      target:
      entity_id: timer.azan_subuh_timer
    • wait_for_trigger:
      • trigger: state
        entity_id: timer.azan_subuh_timer
        to: idle
    • action: remote.send_command
      metadata: {}
      data:
      device: Radio Warna
      command: toggle off
      target:
      entity_id: remote.dining_room_broadlink
    • action: light.turn_off
      target:
      entity_id:
      • light.dining_wled_main
      • light.air_con_wled
      • light.dining_wled_2
        data: {}
        mode: single
        [/wrap]