Android TVSynchronisationDigital SignageKotlinSSERetail Tech

Synchroniser des écrans en magasin : comment nous avons résolu le défi du temps réel

Retour d'expérience sur la conception d'un système de synchronisation multi-écrans pour l'affichage dynamique en magasin Bureau Vallée. Du web embarqué à l'app native Android TV.

4 mars 2026
Mis à jour le 15 mars 2026
MaximeMaxime
Synchroniser des écrans en magasin : comment nous avons résolu le défi du temps réel

En bref

  • 1Synchronisation multi-écrans avec précision de quelques dizaines de millisecondes grâce à une calibration d'horloge inspirée de NTP
  • 2Application native Android TV en Kotlin réduisant la consommation RAM de 150 MB (navigateur) à 30 MB
  • 3Architecture SSE unidirectionnelle pour une mise à jour temps réel sans la complexité des WebSockets

Synchroniser des écrans en magasin : retour d'expérience

Le contexte : quand chaque milliseconde compte

Imaginez un magasin Bureau Vallée. Quatre écrans disposés dans l'espace de vente diffusent des promotions, des vidéos produits, des animations. Le client qui traverse le magasin doit percevoir un ensemble cohérent — pas quatre écrans vivant chacun leur vie avec un décalage visible à l'oeil nu.

C'est exactement le défi que nous avons relevé avec Kody's TV Management, un projet co-développé avec des franchisés Bureau Vallée pour gérer l'affichage dynamique en magasin.

Ce qui semblait simple sur le papier s'est révélé être un problème d'ingénierie fascinant.


La première approche : le navigateur embarqué

Notre première itération reposait sur une approche classique : une application web affichée en plein écran via un navigateur embarqué sur des Xiaomi Mi TV. Le dashboard d'administration poussait les contenus, et le navigateur les affichait.

Ça fonctionnait. Jusqu'à un certain point.

Les limites du web embarqué

  • Consommation mémoire excessive — un navigateur Chromium sur une TV d'entrée de gamme, c'est comme faire tourner un SUV dans une ruelle. Les crashs et redémarrages étaient fréquents.
  • Aucun contrôle hardware — impossible d'éteindre ou rallumer l'écran programmatiquement, de gérer le cycle de vie de l'application, ou de garantir un redémarrage automatique après une coupure de courant.
  • Synchronisation approximative — chaque navigateur chargeait les contenus à son propre rythme. Le décalage entre écrans pouvait atteindre plusieurs secondes.

Le constat était clair : pour un affichage professionnel et fiable, il fallait aller plus bas dans la stack.


Le pivot : une application native Android TV

Nous avons fait le choix de développer une application native en Kotlin avec Jetpack Compose, spécifiquement conçue pour Android TV. Ce choix nous a donné ce qui manquait cruellement :

  • Contrôle total du hardware — gestion de l'allumage/extinction de l'écran via les settings système, wake locks pour empêcher la mise en veille, service foreground persistant.
  • Performance — passage d'environ 150 MB de RAM consommée par le navigateur à ~30 MB pour l'app native. Sur du matériel contraint, ça change tout.
  • Fiabilité — auto-démarrage au boot via un AccessibilityService, récupération automatique après crash ou coupure de courant. L'app est conçue pour ne jamais s'arrêter.
  • Lecture offline — pré-téléchargement et mise en cache des médias pour continuer à diffuser même en cas de perte de connexion réseau.

Mais la vraie victoire technique, c'est sur la synchronisation.


Le coeur du problème : synchroniser sans horloge commune

Le défi fondamental de la synchronisation multi-écrans est simple à énoncer, complexe à résoudre : chaque TV a sa propre horloge, et ces horloges dérivent.

Deux Xiaomi Mi TV posées côte à côte dans le même magasin peuvent avoir un écart de plusieurs secondes entre leurs horloges système. Si chaque écran se fie à son propre System.currentTimeMillis() pour décider quand afficher quel contenu, le résultat est un décalage visible et gênant.

L'approche : s'inspirer de NTP

Plutôt que d'essayer de synchroniser les horloges des TV entre elles (ce qui nécessiterait un réseau local fiable et une découverte de pairs), nous avons adopté une approche inspirée du protocole NTP (Network Time Protocol) — mais simplifiée et adaptée à notre contexte.

L'idée centrale : toutes les TV se synchronisent avec une référence de temps commune côté serveur, plutôt qu'entre elles.

Le mécanisme en quelques mots

  1. Le serveur fait autorité. Chaque réponse API et chaque événement SSE embarque un timestamp serveur. C'est la source de vérité.

  2. Chaque TV calcule son décalage. En comparant l'heure du serveur à sa propre horloge au moment de la réception, chaque appareil estime un clock offset — l'écart entre son horloge locale et l'horloge du serveur.

  3. Filtrage statistique. Un seul échantillon ne suffit pas — la latence réseau varie. Nous accumulons plusieurs échantillons et appliquons un filtre pour éliminer les valeurs aberrantes et converger vers un offset stable.

  4. Temps virtuel partagé. Une fois calibrée, chaque TV utilise une fonction serverNow() qui retourne non pas l'heure locale, mais l'heure estimée du serveur. Toutes les décisions de scheduling — quand démarrer un slot, quand transitionner vers le contenu suivant — sont prises sur cette base commune.

Le résultat : une précision de synchronisation de l'ordre de quelques dizaines de millisecondes entre les écrans d'un même magasin. Suffisamment précis pour que l'oeil humain ne perçoive aucun décalage.


La couche temps réel : SSE plutôt que WebSocket

Pour la communication serveur-vers-TV, nous avons choisi Server-Sent Events (SSE) plutôt que WebSocket. Ce choix peut surprendre, mais il est délibéré :

  • Unidirectionnel par design — les TV reçoivent des instructions, elles n'en envoient pas (ou très peu). SSE colle parfaitement à ce modèle.
  • Reconnexion native — le protocole SSE gère automatiquement les reconnexions. Sur une TV qui peut perdre le WiFi ponctuellement, c'est un avantage majeur.
  • Simplicité d'infrastructure — pas besoin de gérer un état de connexion bidirectionnel. Notre passerelle SSE tourne sur une instance EC2 minimale derrière CloudFront.

Quand un administrateur modifie une timeline ou une plage horaire depuis le dashboard, un événement est immédiatement propagé à toutes les TV concernées. Pas de polling, pas de délai.

Le flux SSE sert aussi de canal pour la synchronisation d'horloge continue : des événements périodiques permettent aux TV de recalibrer leur offset en temps réel.


La gestion à distance : le tunnel ADB

Un problème souvent sous-estimé dans le Digital Signage : comment maintenir des appareils déployés dans des magasins sans accès physique ?

Les TV sont sur des réseaux locaux de magasins. Pas de VPN, pas d'IP publique, pas d'accès SSH. Pour installer une mise à jour ou diagnostiquer un problème, il fallait historiquement se déplacer.

Nous avons résolu ce problème avec un tunnel ADB (Android Debug Bridge) via WebSocket : chaque TV maintient une connexion sortante vers un relais cloud. Depuis notre infrastructure, nous pouvons ouvrir un canal ADB à travers ce tunnel pour installer des APK, accorder des permissions, ou collecter des logs — le tout sans jamais toucher au réseau local du magasin.


L'enregistrement simplifié : le QR code

Ajouter une nouvelle TV au système doit être trivial pour un responsable de magasin. Notre flux :

  1. L'app démarre et affiche un QR code unique sur l'écran
  2. L'administrateur scanne ce QR depuis le dashboard web
  3. La TV est automatiquement associée au bon magasin et au bon groupe d'écrans
  4. Le contenu commence à se télécharger et la diffusion démarre

Zéro configuration technique. Zéro intervention IT.


L'architecture globale

Le système complet se compose de trois briques :

L'app Android TV (Kotlin)

Le client natif installé sur chaque écran. Gère la lecture des médias (vidéos via ExoPlayer, images, contenus web), le scheduling basé sur le temps serveur, le cache offline, et le contrôle hardware.

Le dashboard web (Next.js)

L'interface d'administration pour les équipes terrain et les franchisés. Permet de gérer les contenus, programmer les timelines, contrôler les plages d'allumage/extinction, et monitorer l'état du parc d'écrans.

La passerelle temps réel (Bun / SSE)

Le lien entre le dashboard et les TV. Diffuse les mises à jour en temps réel et assure la synchronisation d'horloge continue. Déployée sur AWS avec une empreinte minimale.

L'ensemble de l'infrastructure est gérée via Terraform et déployée sur AWS (Amplify, CloudFront, S3, EC2, RDS), avec un focus constant sur l'optimisation des coûts — instances ARM, Spot quand possible, dimensionnement minimal.


Les enseignements

Ce projet nous a rappelé plusieurs vérités :

Le temps est relatif. Même sur un réseau local, synchroniser des horloges est un problème non trivial. L'humilité face à ce constat nous a poussés vers une architecture robuste plutôt qu'une solution "ça devrait marcher".

Le natif a encore sa place. Dans un monde dominé par le web et le cross-platform, développer une app native Android TV en Kotlin était un choix radical. Mais quand on a besoin de contrôler du hardware, de garantir une fiabilité 24/7, et d'optimiser chaque mégaoctet de RAM, le natif reste imbattable.

La simplicité d'usage cache la complexité technique. Un QR code scanné, et la TV diffuse. Derrière cette simplicité : un système de calibration d'horloge, un tunnel ADB cloud, un cache de médias avec politique d'expiration, un service foreground persistant avec wake locks. L'iceberg classique.

L'infrastructure, c'est du code. Terraform nous a permis de reproduire l'environnement à l'identique, de maîtriser les coûts, et de documenter l'infrastructure aussi rigoureusement que le code applicatif.


Et maintenant ?

Le système tourne en production dans plusieurs points de vente Bureau Vallée. Les retours terrain sont positifs : les équipes trouvent le dashboard intuitif, les écrans sont fiables, et les mises à jour de campagnes se propagent en quelques secondes.

Les prochaines étapes ? Élargir le parc d'écrans, affiner encore la synchronisation, et explorer de nouvelles formes de contenus interactifs. Le Digital Signage n'a pas fini de se réinventer — et nous non plus.

Questions fréquentes

SSE (Server-Sent Events) est unidirectionnel par design, ce qui correspond parfaitement à notre cas d'usage où les TV reçoivent des instructions sans en envoyer. Le protocole gère nativement les reconnexions, crucial pour des TV qui peuvent perdre le WiFi ponctuellement.

Vous avez aimé cet article ?