No description
Find a file
2026-05-17 02:41:57 +02:00
.github/workflows fix: update action versions and enhance service descriptions in JMAP integration 2026-05-17 01:58:00 +02:00
blueprints/automation/jmap fix: correctness pass on blueprints, services, and coordinator 2026-05-17 01:53:45 +02:00
custom_components/jmap feat: enhance JMAP push event handling and improve latest email retrieval logic 2026-05-17 02:41:57 +02:00
tests chore: init Home Assistant JMAP integration 2026-05-17 01:41:09 +02:00
.gitignore chore: init Home Assistant JMAP integration 2026-05-17 01:41:09 +02:00
hacs.json fix: update action versions and enhance service descriptions in JMAP integration 2026-05-17 01:58:00 +02:00
LICENSE chore: init Home Assistant JMAP integration 2026-05-17 01:41:09 +02:00
pytest.ini chore: init Home Assistant JMAP integration 2026-05-17 01:41:09 +02:00
README.md fix: update README to clarify mailbox sensor naming and configuration options 2026-05-17 02:28:21 +02:00
requirements_test.txt fix: correctness pass on blueprints, services, and coordinator 2026-05-17 01:53:45 +02:00

JMAP Mail for Home Assistant

A first-class Home Assistant integration for any JMAP server (Stalwart, Fastmail, Cyrus, Apache James, …). JMAP's native push channel means new-mail events arrive in HA within a second or two — no IMAP IDLE polling, no SMTP relay required.

What you get

  • Push via JMAP EventSource. Falls back to polling automatically.
  • Send mail through HA's notify entity or the jmap.send_email service, including HTML, CC/BCC, custom headers, and attachments.
  • Sensors for unread/total per mailbox, latest sender, latest subject. Names reflect the full mailbox path (e.g. Inbox > Promotions > Travel unread).
  • Binary sensors for "has unread" per account and per mailbox.
  • Triggers via the jmap_new_email, jmap_email_deleted, and jmap_mailbox_changed events.
  • Services to mark read/unread, flag, archive, move, delete, search.
  • Multi-account — add more than one entry, each is its own device.
  • Reverse-proxy friendly — session URLs are re-anchored to the origin you actually connected to, so a server that advertises an internal :8080 API URL still works when you reach it via https://mail.example.com.
  • Diagnostics with credentials redacted.

Install

  1. HACS → Integrations → ⋮ → Custom repositories.
  2. Add https://github.com/bulwarkmail/homeassistant-jmap as type Integration.
  3. Install JMAP Mail, restart Home Assistant.
  4. Settings → Devices & Services → Add IntegrationJMAP Mail.

Manual

Copy custom_components/jmap into your HA config/custom_components/ directory and restart.

Configure

You'll need:

  • Server URL — usually the base URL of your mail server. The integration will probe /.well-known/jmap if you give it a bare hostname. You can also paste the full session URL directly (e.g. https://mail.example.com/.well-known/jmap).
  • Bearer token or username + password. Bearer tokens are preferred where supported; for Fastmail use an app password.

Once added, open Configure on the integration to:

  • Change the poll interval and toggle push.
  • Set a default From name and identity.
  • Pick additional mailboxes to expose as devices/sensors. Only the inbox and the mailboxes you select here will get unread/total/has-unread entities; everything else stays out of the UI until you opt in.

Use it

Trigger an automation on new mail

automation:
  - alias: "Ring doorbell email → porch lights on"
    trigger:
      - platform: event
        event_type: jmap_new_email
        event_data:
          # event_data fields: account, from, from_name, subject, preview,
          # mailbox_names, has_attachment, is_unread, ...
    condition:
      - condition: template
        value_template: >-
          {{ 'no-reply@ring.com' in trigger.event.data.from
             and 'motion' in trigger.event.data.subject | lower }}
    action:
      - service: light.turn_on
        target: { entity_id: light.porch }
        data: { brightness_pct: 100, color_temp: 250 }
      - service: notify.mobile_app_pixel
        data:
          message: "Ring: {{ trigger.event.data.subject }}"

Send a notification as email

script:
  email_household:
    sequence:
      - service: notify.send_message
        target:
          entity_id: notify.you_at_example_com_email
        data:
          message: "Water leak detected in the laundry."
          title: "🚨 Leak alert"
          data:
            to: ["alice@example.com", "bob@example.com"]
            html: "<h1>Leak alert</h1><p>Sensor: {{ states('sensor.laundry_leak') }}</p>"

Or the explicit service:

service: jmap.send_email
data:
  to: ["family@example.com"]
  subject: "Solar production today"
  body: "Generated {{ states('sensor.solar_today') }} kWh"
  attachments:
    - /config/www/solar-chart.png

Daily 8am digest

automation:
  - alias: "Morning inbox briefing"
    trigger: { platform: time, at: "08:00:00" }
    action:
      - service: jmap.search
        data: { query: "is:unread", limit: 10 }
        response_variable: result
      - service: tts.cloud_say
        data:
          entity_id: media_player.kitchen
          message: >-
            You have {{ result.emails | count }} unread emails.
            Most recent from {{ result.emails[0].from_name or result.emails[0].from[0] }}:
            {{ result.emails[0].subject }}.

Mark mail as read from a dashboard

Bind a button to the service:

service: jmap.mark_read
data:
  email_id: "{{ state_attr('sensor.you_at_example_com_latest_sender', 'email_id') }}"

Events

jmap_new_email

account: "you@example.com"
entry_id: "01HX..."
email_id: "M..."
thread_id: "T..."
mailbox_ids: ["mb-inbox-id"]
mailbox_names: ["Inbox"]
from: ["alice@example.com"]
from_name: "Alice"
to: ["you@example.com"]
subject: "Pull request review request"
preview: "Hi, could you take a look at..."
received_at: "2026-05-17T08:14:02Z"
has_attachment: false
is_unread: true
is_flagged: false

jmap_email_deleted

account: "you@example.com"
entry_id: "01HX..."
email_id: "M..."

jmap_mailbox_changed

Fired whenever an unread or total count changes on a known mailbox. Useful for "mailbox emptied" automations.

Blueprints

Three drop-in blueprints ship in blueprints/:

  • package_delivery_alert.yaml — flash a light when a parcel-tracking email lands.
  • ring_email_lights.yaml — turn lights on when a Ring motion email arrives.
  • daily_inbox_summary.yaml — daily digest via TTS or mobile push.

Permissions

Attachments are restricted to paths under config/ or any directory you've added to homeassistant.allowlist_external_dirs — the same rules HA uses for shell_command and notify.smtp.

Limitations

  • The integration uses a single Email state token per account. After very long outages (state expiry), the next refresh resyncs from the latest inbox rather than reconstructing every missed message.
  • JMAP push pings every 5 minutes by default; if you sit behind a proxy that closes idle connections, the integration auto-reconnects with backoff.
  • Calendar/contacts (JSCalendar/JSContact) are out of scope for v1.

License

MIT. See LICENSE.