Your cinema’s showtimes in Home Assistant: a sensor, a card and a notification

YAML only, no custom integration: a template sensor fetches the day’s showtimes every four hours, a Markdown card lists the ones still to come with booking links, and an automation tells you at five what is on tonight.

5 min read

On this page
  1. What you need
  2. 1.The key in secrets.yaml
  3. 2.The sensor
  4. 3.The dashboard card
  5. 4.A notification when something is on tonight
  6. Quota
  7. Variants

Your Home Assistant already knows the weather, the lights and the bins. This tutorial teaches it the programme of your local cinema: a sensor that holds the day's showtimes, a dashboard card that lists the ones still to come with their booking links, and a notification at five o'clock when something is on tonight. Everything is YAML pasted into Home Assistant; no custom integration, no HACS, no add-on.

Today at Le Champo

Affreux, sales et méchants
15:40 VOST · 19:55 VOST

Safe
15:45 VOST

LA MAISON AU PIED DU ROC
20:00 VOST

What you need

  • Home Assistant 2024.10 or later, with access to configuration.yaml (the File editor or Studio Code Server add-on, or SSH). This tutorial was run on Home Assistant 2026.11.
  • An API Cinéma key: create a free account, confirm your e-mail address, then create a key from the dashboard. The free plan gives 100 requests a week; this setup uses about 42.
  • The slug of the cinema. Search it by name:
Terminal
curl "https://api.api-cinema.com/v1/cinemas?q=champo" -H "Authorization: Bearer ac_live_…"

The answer gives "slug": "le-champo-paris-5e-arrondissement". Any cinema of the API works: France, the United States, Australia.

The key in secrets.yaml

Home Assistant keeps passwords out of the configuration in secrets.yaml, next to configuration.yaml. Add one line, with the word Bearer, a space, then your key:

secrets.yaml
api_cinema_auth: Bearer ac_live_…

The sensor

Two blocks go in configuration.yaml. The first is a rest_command: a named HTTP request that Home Assistant can run, here the showtimes of one cinema for today, with the film of each showtime (include=movie). The second is a template sensor that runs that request when Home Assistant starts and every four hours, and keeps a short list of the day's showtimes: time, film, version, booking link.

configuration.yaml
rest_command:
  cinema_showtimes:
    url: 'https://api.api-cinema.com/v1/cinemas/le-champo-paris-5e-arrondissement/showtimes?date={{ now().date() }}&include=movie&limit=1000'
    headers:
      Authorization: !secret api_cinema_auth
    timeout: 20

template:
  - triggers:
      - trigger: homeassistant
        event: start
      - trigger: time_pattern
        hours: /4
        minutes: 1
    actions:
      - action: rest_command.cinema_showtimes
        response_variable: answer
        continue_on_error: true
    sensor:
      - name: Cinema today
        unique_id: cinema_today
        icon: mdi:filmstrip
        unit_of_measurement: showtimes
        state: >
          {% if answer is defined and answer.status == 200 %}
            {{ answer.content.data | count }}
          {% else %}
            {{ this.state }}
          {% endif %}
        attributes:
          showtimes: >
            {% if answer is defined and answer.status == 200 %}
              {% set ns = namespace(list=[]) %}
              {% for s in answer.content.data %}
                {% set ns.list = ns.list + [{
                  'time': s.startsAt[11:16],
                  'film': s.movie.title,
                  'version': s.version if s.format == '2D' else s.version ~ ' ' ~ s.format,
                  'url': s.bookingUrl
                }] %}
              {% endfor %}
              {{ ns.list }}
            {% else %}
              {{ this.attributes.showtimes | default([]) }}
            {% endif %}

Replace the slug in the URL with your cinema's. If your configuration.yaml already has a template: key, add the - triggers: item under the existing one instead of writing a second template:.

A few details that matter:

  • date={{ now().date() }} asks for the whole day in the cinema's local time. The time pattern fires at 00:01, 04:01, 08:01… so the list switches to the new day just after midnight.
  • The sensor never goes blank. When the request fails (no network, wrong key, a 429 when the weekly quota is used up) the action does not stop the template (continue_on_error), and the state and the list keep their last values (this.state, this.attributes). Home Assistant writes the error in its log.
  • startsAt is the cinema's wall-clock time, so startsAt[11:16] is the time printed on the ticket (19:55), without any time zone conversion.

Check the configuration (Developer tools › YAML › Check configuration), then restart Home Assistant. In Developer tools › States, sensor.cinema_today shows the number of showtimes of the day, and its showtimes attribute the list:

showtimes:
  - time: "15:40"
    film: Affreux, sales et méchants
    version: VOST
    url: https://www.cinema-lechampo.com/film.html#/media/574?showId=34629
  - time: "15:45"
    film: Safe
    ...

The dashboard card

Edit a dashboard, Add card › Markdown, switch to the code editor and paste this. The template groups the showtimes still to come by film, puts the film with the next showtime first, and links each time to the cinema's booking page:

Markdown card
type: markdown
title: Today at Le Champo
content: |
  {% set left = state_attr('sensor.cinema_today', 'showtimes') | default([], true)
     | selectattr('time', '>=', now().strftime('%H:%M')) | list %}
  {% for film, times in left | groupby('film') | sort(attribute='list.0.time') %}
  **{{ film }}**
  {% for s in times %}{{ '[' ~ s.time ~ '](' ~ s.url ~ ')' if s.url else s.time }} {{ s.version }}{{ ' · ' if not loop.last }}{% endfor %}

  {% else %}
  Nothing left today.
  {% endfor %}

The card renders again on its own every minute, because the template uses now(): a showtime disappears once it has started, with no new request to the API. Keep content: | with a pipe: with >, YAML folds the lines and the films run into each other.

A notification when something is on tonight

Settings › Automations › Create automation › Create new automation, then ⋮ › Edit in YAML, and paste:

Automation
alias: Tonight at the cinema
triggers:
  - trigger: time
    at: '17:00:00'
variables:
  evening: >
    {{ state_attr('sensor.cinema_today', 'showtimes') | default([], true)
       | selectattr('time', '>=', '18:00') | list }}
conditions:
  - condition: template
    value_template: '{{ evening | count > 0 }}'
actions:
  - action: persistent_notification.create
    data:
      title: Tonight at Le Champo
      message: |
        {% for film, times in evening | groupby('film') | sort(attribute='list.0.time') -%}
        - {{ film }}: {{ times | map(attribute='time') | join(', ') }}
        {% endfor %}

At five, if at least one showtime starts at 18:00 or later, a notification appears in Home Assistant:

Tonight at Le Champo
- Affreux, sales et méchants: 19:55
- LA MAISON AU PIED DU ROC: 20:00
- Rendez-vous avec la peur: 22:05

No showtime tonight, no notification. To get it on your phone, replace the action with the one of the Companion app, notify.mobile_app_<your_phone>, with the same title and message. The automation reads the sensor and sends no request of its own; ⋮ › Run actions tests it at any hour.

Quota

The sensor sends one request when Home Assistant starts and one every four hours: six a day, about 42 a week, whatever the number of cards and automations that read it. The free plan covers that with room for restarts. A hours: /2 pattern doubles it; stay above two hours on the free plan.

Variants

  • Several cinemas. Copy the rest_command and the - triggers: item, with another name (cinema_showtimes_2, Cinema today 2), and point a second card at sensor.cinema_today_2. Each cinema adds 42 requests a week.
  • Every cinema of a town. Use https://api.api-cinema.com/v1/showtimes?city=Lyon&date={{ now().date() }}&include=movie,cinema&limit=1000 and add 'cinema': s.cinema.name to each item of the list, then show it in the card next to the time. Lyon has some 400 showtimes a day: see the next point.
  • Original version only. Add &version=VOST (or VO) to the URL; or keep the URL and filter in the card with | selectattr('version', 'search', 'VO').
  • A multiplex. A cinema with 150 showtimes a day makes a list larger than the 16 KB Home Assistant keeps in its history: it logs a warning and stores the state without the list, and the card keeps working. To silence it, exclude the sensor from the recorder (recorder: exclude: entities: [sensor.cinema_today]).

Every parameter and field is in the documentation. For the same programme as a daily e-mail, see the n8n newsletter; to ask an assistant "what's on tonight?" in plain words, the MCP server answers from Claude or Cursor.