Okilu

Branchez Okilou à votre maison

Une intégration Home Assistant officielle (HACS), une API de lecture gratuite, une spec OpenAPI pour construire le reste. Okilou fournit les faits ; ce que votre maison en fait vous appartient.

Gratuite · lecture seule · révocable

Le principe

Okilou expose des faits : les minutes débloquées, la session en cours, la récompense qui vient d'être utilisée. Nous ne pilotons aucun appareil et nous ne savons rien de votre installation.

🔒 Lecture seule

Le jeton ne peut rien valider. Même perdu, il ne permet pas de tricher : l'app reste le seul endroit où l'on valide un effort.

⏱️ La fin de session est connue d'avance

endsAt est fourni dès le lancement : votre automatisation programme la fin à la minute près, sans minuteur fragile.

🏠 Rien à ouvrir sur votre box

C'est votre Home Assistant qui vient lire Okilou (sortant, HTTPS). Aucun port à ouvrir, aucune adresse à exposer.

1. Créez votre jeton

Dans l'app : Réglages → Ton foyer → API & domotique → Créer un jeton. Il s'affiche une seule fois (copiez-le), et se révoque au même endroit à tout moment. Un jeton couvre votre foyer : tous vos enfants, avec le même jeton. En garde alternée indépendante, le jeton de votre maison ne voit que vos journées : les jours chez l'autre parent, /today répond calmement { "isMyDay": false } (pas d'erreur), et l'autre maison crée son propre jeton.

# Base
https://api.okilou.com/functions/v1/api

# Toutes les requêtes portent le jeton
curl -H "Authorization: Bearer rf_api_…" $BASE/children
RouteCe qu'elle renvoie
GET /childrenles enfants du foyer couverts par le jeton (id, prénom)
GET /children/:id/todayminutes débloquées / consommées / restantes, points, comportements du jour, session { running, endsAt, minutes }, série de jours réussis, isMyDay
GET /children/:id/events?since=…les faits pour vos déclencheurs : behavior_validated, session_started, menu_item_consumed (avec le libellé, ex. « Soirée film »), cheer_sent

Limite : 30 requêtes/minute par jeton. Les jetons créés avant l'ouverture de api.okilou.com fonctionnent tels quels : l'ancienne adresse …supabase.co/functions/v1/api reste valable.

2. L'intégration Home Assistant (HACS)

Le chemin recommandé : tout est fait pour vous. Capteurs par enfant, événements sur le bus, curseur persistant (aucun rejeu au redémarrage), garde alternée gérée, re-connexion guidée si vous révoquez le jeton. Code ouvert (MIT) : github.com/okilou/okilou-homeassistant.

  1. HACS → menu ⋮ → Custom repositories → ajouter https://github.com/okilou/okilou-homeassistant (type Integration) ;
  2. Installer Okilou, redémarrer Home Assistant ;
  3. Paramètres → Appareils et services → Ajouter une intégration → Okilou → coller le jeton.

Vous obtenez un appareil par enfant :

EntitéContenu
sensor.…_minutes_de_recompense_restantesla jauge de l'app (min) : gagné, utilisé, joker/gelé, prochain palier en attributs
sensor.…_points_du_jourpoints + détail des comportements en attributs
sensor.…_fin_de_la_sessionhorodatage de fin : HA affiche « dans 23 min » tout seul
sensor.…_serie_de_jours_reussisla série du Parcours
binary_sensor.…_session_de_recompensesession en cours (ends_at, minutes en attributs)
binary_sensor.…_a_la_maison_aujourd_huigarde alternée : l'enfant est-il chez vous aujourd'hui ?

Et chaque fait nouveau arrive en événement okilou_event sur le bus, avec childName (le prénom) en plus des champs de l'API : parfait pour les annonces. Distinction utile pour vos automatisations : une récompense en temps lancée dans l'app émet session_started (avec minutes) ; une récompense à la carte du menu (« Soirée film ») émet menu_item_consumed (avec label).

automation:
  - alias: "Okilou · annonce du bravo"
    triggers:
      - trigger: event
        event_type: okilou_event
        event_data: { type: behavior_validated }
    actions:
      - action: tts.speak
        target: { entity_id: tts.home }
        data:
          media_player_entity_id: media_player.salon
          message: "Bravo {{ trigger.event.data.childName }} — {{ trigger.event.data.label }} !"
# La console s'ouvre au lancement d'une session, et s'éteint à l'heure dite
automation:
  - alias: "Okilou · session"
    triggers:
      - trigger: event
        event_type: okilou_event
        event_data: { type: session_started }
    actions:
      - action: switch.turn_on
        target: { entity_id: switch.prise_console }

  - alias: "Okilou · fin de session"
    triggers:
      - trigger: template
        value_template: "{{ states('sensor.nino_fin_de_la_session') != 'unknown' and as_timestamp(states('sensor.nino_fin_de_la_session')) <= as_timestamp(now()) }}"
    actions:
      - # à vous de choisir : notification, scène, extinction…
      - action: notify.mobile_app_parent
        data: { message: "La session de Nino est terminée." }

Latence : l'app synchronise en 4 à 60 s, l'intégration interroge toutes les 60 s ; comptez 30 s à 2 min entre un geste dans l'app et sa trace dans Home Assistant. Le README du dépôt contient d'autres recettes et un exemple de tableau de bord.

3. Sans l'intégration : YAML pur

Vous préférez tout câbler vous-même (ou vous n'utilisez pas HACS) ? Un capteur rest et un « pont d'événements » suffisent ; ces recettes restent entièrement fonctionnelles. ⚠️ Les noms d'entités de cette section (sensor.okilou_nino…) sont ceux que vous créez ci-dessous : ne les mélangez pas avec ceux de l'intégration de la section 2 (sensor.nino_…), qui n'existent pas en YAML pur.

Le secret, une fois pour toutes

Le jeton vit dans secrets.yaml, préfixe compris (la syntaxe !secret ne s'interpole pas à l'intérieur d'une chaîne) :

# secrets.yaml
okilou_token: "Bearer rf_api_…"

Le capteur de base (la jauge) : un par enfant, même jeton

# configuration.yaml — forme moderne (rest: de haut niveau)
rest:
  - resource: "https://api.okilou.com/functions/v1/api/children/<ID>/today"
    headers:
      Authorization: !secret okilou_token
    scan_interval: 60
    sensor:
      - name: "Okilou Nino"
        # default(0) : en garde alternée, les jours « autre maison » (isMyDay: false)
        # n'envoient pas minutesRemaining — sans lui, la jauge passerait à 'unknown'.
        value_template: "{{ value_json.minutesRemaining | default(0) }}"
        unit_of_measurement: "min"
        json_attributes:
          - minutesUnlocked
          - session
          - streakDays
          - behaviors

Le pont d'événements

Il relève /events pour chaque enfant, ré-émet chaque fait dans Home Assistant (avec child pour router), et mémorise un curseur par enfant. Un seul enfant ? Gardez une seule entrée dans la liste.

Au premier lancement, donnez à chaque curseur la date du jour (ex. 2026-07-22T00:00:00Z), sinon il rejouera l'historique récent. ⚠️ Posez cette valeur une fois à la main (via l'UI ou le service input_text.set_value), jamais avec initial: : ce paramètre désactive la restauration d'état, et chaque redémarrage de Home Assistant rejouerait la journée.

# configuration.yaml
input_text:
  okilou_cursor_lola:
    name: "Curseur Okilou · Lola"
  okilou_cursor_angelo:
    name: "Curseur Okilou · Angelo"

rest_command:
  okilou_events:
    # urlencode : le curseur composite contient une virgule
    url: "https://api.okilou.com/functions/v1/api/children/{{ child }}/events?since={{ states(cursor) | urlencode }}"
    headers:
      Authorization: !secret okilou_token

automation:
  - alias: "Okilou · Pont événements"
    mode: single
    # silencieux si un cycle chevauche le précédent (pic réseau) — pas de log d'erreur
    max_exceeded: silent
    trigger:
      - platform: time_pattern
        seconds: "/30"
    action:
      - repeat:
          for_each:
            - { child: "<ID_LOLA>", cursor: input_text.okilou_cursor_lola }
            - { child: "<ID_ANGELO>", cursor: input_text.okilou_cursor_angelo }
          sequence:
            # capturé AVANT la boucle interne : dedans, repeat.item désigne l'événement
            - variables:
                cursor_entity: "{{ repeat.item.cursor }}"
            - service: rest_command.okilou_events
              data:
                child: "{{ repeat.item.child }}"
                cursor: "{{ repeat.item.cursor }}"
              response_variable: resp
            - choose:
                - conditions: "{{ resp.status == 200 and (resp.content.events | count) > 0 }}"
                  sequence:
                    - repeat:
                        for_each: "{{ resp.content.events }}"
                        sequence:
                          - event: okilou_event
                            event_data:
                              type: "{{ repeat.item.type }}"
                              child: "{{ repeat.item.childId }}"
                              label: "{{ repeat.item.label | default('') }}"
                              minutes: "{{ repeat.item.minutes | default(0) }}"
                    - service: input_text.set_value
                      target: { entity_id: "{{ cursor_entity }}" }
                      data:
                        value: "{{ resp.content.cursor }}"

🎬 « Soirée film » → le vidéoprojecteur s'allume

La récompense à la carte que l'enfant vient d'utiliser déclenche la maison. C'est votre menu de récompenses qui pilote vos appareils.

automation:
  - alias: "Okilou · Soirée film"
    trigger:
      - platform: event
        event_type: okilou_event
        event_data:
          type: menu_item_consumed
          label: "Soirée film 🍿"
    action:
      - service: media_player.turn_on
        target: { entity_id: media_player.videoprojecteur }
      - service: light.turn_on
        target: { entity_id: light.salon }
        data: { brightness_pct: 15 }

🎮 La session pilotée par un timer

La durée arrive avec l'événement (minutes) : un timer programme la suite ; contrairement à un delay, il survit à un redémarrage de Home Assistant. À la fin, votre maison fait ce que vous avez décidé : notification, scène, extinction de la prise.

# configuration.yaml
timer:
  okilou_session:
    name: "Session Okilou"
    restore: true

automation:
  - alias: "Okilou · Session"
    trigger:
      - platform: event
        event_type: okilou_event
        event_data: { type: session_started }
    condition:
      - condition: template
        value_template: "{{ (trigger.event.data.minutes | int(0)) > 0 }}"
    action:
      - service: switch.turn_on
        target: { entity_id: switch.prise_console }
      - service: timer.start
        target: { entity_id: timer.okilou_session }
        data:
          duration: "{{ trigger.event.data.minutes | int(0) * 60 }}"

  - alias: "Okilou · Fin de session"
    trigger:
      - platform: event
        event_type: timer.finished
        event_data: { entity_id: timer.okilou_session }
    action:
      - service: switch.turn_off
        target: { entity_id: switch.prise_console }

📊 La jauge sur le tableau de bord familial

# Carte Lovelace — la jauge du frigo connecté
type: gauge
entity: sensor.okilou_nino
name: "Minutes de Nino"
min: 0
max: 60
severity: { green: 20, yellow: 10, red: 0 }

4. La spec OpenAPI : construisez le vôtre

L'API est décrite en OpenAPI 3.1 : okilou.fr/api/openapi.yaml (aussi sur GitHub). Trois routes, un schéma précis pour chaque réponse, de quoi outiller n'importe quel projet :

🧪 Explorer

Importez le fichier dans Insomnia, Postman ou l'éditeur editor.swagger.io : les routes, paramètres et réponses types apparaissent prêts à tester avec votre jeton.

⚙️ Générer un client

Un client typé en une commande : par exemple npx openapi-typescript openapi.yaml (TypeScript) ou openapi-generator-cli (Python, C#, Go, Rust…).

💚 Projets bienvenus

Un widget Windows (barre des tâches), un module Linux (Waybar, GNOME), un plugin Jeedom, une carte Lovelace dédiée… Ouvrez une issue : on référence votre projet ici.

Idées de départ : un widget de bureau qui affiche la jauge du jour (poll /today toutes les 60 s) ; une icône systray qui passe au vert quand session.running ; un mode « ne pas déranger » d'ordinateur familial piloté par /events. Lecture seule, 30 req/min, curseur composite pour ne jamais rejouer un événement : tout est dans la spec.

⚖️ L'esprit

Okilou est l'arbitre : il tient le compte des règles convenues, en lecture seule. L'app reste le seul endroit où l'on valide. Ce que votre maison fait de ces faits (célébrer, programmer, éteindre à l'heure dite), c'est chez vous, c'est vous qui décidez.

Une question, un besoin d'endpoint ? Écrivez-nous : contact@okilou.fr
Les jetons sont compatibles avec toutes les versions de l'API : ceux créés hier fonctionnent demain.