Agency-Watch Apps Personnalisées
Désormais avec AgencyOS27 : une mise à jour gratuite pour tous ceux qui ont déjà la montre.

⌚ Créer votre propre app pour l'Agency-Watch
La montre est ouverte. Un propriétaire de serveur peut ajouter une app qui ressemble à une app intégrée et se comporte comme elle, avec les mêmes composants, les mêmes couleurs et les mêmes traductions.
Condition : l'édition Pro. Les apps personnalisées n'existent que si l'Agency-Phone tourne sur le serveur et que le joueur le possède. Sans Pro, elles sont enregistrées mais jamais livrées : l'interface n'en sait rien, elles n'apparaissent pas dans l'App Store, et le serveur refuse toutes les actions qu'elles envoient. C'est vérifié à trois endroits distincts et ne peut pas être désactivé.
| Fichier | Requis | Rôle |
|---|---|---|
apps/<id>/app.lua | oui | Enregistre l'app : nom, icône, couleur, catégorie. |
apps/<id>/app.js | oui | L'écran : ce qu'il dessine et comment il réagit. |
apps/<id>/app.css | non | Votre propre style. Chargé uniquement si style = true. |
apps/<id>/server.lua | non | Logique serveur que votre app peut appeler. |
Le dossier porte exactement le nom de l'id de l'app. C'est ainsi que la montre trouve app.js et app.css sans que le chemin soit écrit deux fois, et des chemins écrits deux fois finissent tôt ou tard par diverger.
Tout ce qui se trouve sous apps/ est dans escrow_ignore : cela reste lisible et modifiable sur votre disque, et une mise à jour de la montre ne l'écrase jamais.
1 Enregistrer l'app
apps/hello/app.lua
AgencyWatchApp({
id = 'hello', -- lowercase, digits and _, must match the folder
name = 'Hello',
icon = 'raster', -- one of the built-in icons, see below
color = '#5ac8fa', -- #rrggbb
category = 'tools', -- tools | games | driving | style | connected
style = true, -- also load app.css
texts = { en = 'Hello', de = 'Hallo' }, -- optional, name per language
})
Une app avec un id mal formé, sans nom, avec une catégorie inconnue ou une couleur qui n'est pas #rrggbb est refusée dans la console serveur, clairement et avec une raison. Il n'existe pas d'enregistrement à moitié : ce genre d'erreur n'apparaîtrait sinon qu'au moment où un joueur touche l'app.
Lancez refresh et restart Agency-Watch après avoir ajouté des fichiers. Au démarrage, la montre indique ce qui a été enregistré.
2 Dessiner l'écran
apps/hello/app.js
AgencyWatch.app('hello', {
render: function (w) { // required, returns HTML
var d = w.data();
return w.card('Hello, ' + w.esc(d.name)) +
w.section('Steps') +
w.row({ icon: 'schritte', static: true,
top: String(d.steps || 0) }) +
w.button('Roll a die', ' data-roll');
},
bind: function (w) { // optional, runs right after
w.click('[data-roll]', function () {
w.toServer('roll', { sides: 6 }).then(function (a) {
if (a.ok) w.notify('Hello', 'You rolled ' + a.data.value);
});
});
},
});
render s'exécute à chaque construction de l'écran, il ne garde donc aucun état entre deux appels. Ce qui doit survivre à un nouveau rendu va dans une variable en dehors de l'objet de l'app.
Les deux fonctions tournent dans un filet de sécurité. Si votre code lève une erreur, votre app affiche une ligne d'erreur et la montre continue de fonctionner ; l'erreur réelle part dans la console F8.
3 La boîte à outils
w contient tout ce dont une app de montre a besoin, et rien de plus. Une interface qui sait tout faire ne peut plus jamais être modifiée, celle-ci est donc volontairement réduite.
| Appel | Ce que vous obtenez |
|---|---|
w.data() | Un instantané à plat : time, hour, minute, date, weekday, name, job, health, armour, pulse, steps, distance, calories, speed, heading, weather, pro. |
w.row({icon, top, bottom, end, attrs, static}) | Une ligne de liste dans le style de la montre. |
w.section(text) | Un titre de section. |
w.card(html) | Une surface. |
w.button(text, attrs) | Un bouton. attrs est une chaîne d'attributs brute, par ex. ' data-roll'. |
w.toggle(on) | Un interrupteur marche/arrêt. |
w.center(html) | Centré, pour une seule grande valeur. |
w.icon(name) | Une des icônes intégrées. |
w.esc(text) | Échappe le texte. Utilisez-le pour tout ce qui ne vient pas de vous. |
w.text(key, fallback) | Une traduction issue de la montre. |
w.click(selector, fn) | Attache un écouteur. Uniquement dans bind. |
w.notify(title, text, icon, colour) | Une notification sur la montre. |
w.refresh() | Redessine, sans l'animation d'entrée. |
w.close() | Revient en arrière, comme la flèche de l'en-tête. |
w.toServer(action, data) | Interroge le serveur. Renvoie une promesse. |
w.id, w.version | L'id de votre app et la version d'AgencyOS. |
L'instantané est une copie, pas une référence. La montre contient aussi les contacts, les messages et les données de carte ; rien de tout cela n'y figure.
4 Le côté serveur
apps/hello/server.lua
AgencyWatchAction('hello', 'roll', function(src, data)
local sides = tonumber(data and data.sides) or 6
if sides < 2 then sides = 2 end
if sides > 100 then sides = 100 end
return { value = math.random(1, math.floor(sides)) }
end)
w.toServer se résout toujours, avec { ok: true, data: … } ou { ok: false, reason: '…' }, même si le serveur n'a jamais répondu. Votre app ne peut jamais rester bloquée à attendre, et la raison se trouve toujours au premier niveau, quel que soit le côté qui a refusé.
reason | Signification |
|---|---|
no_pro | Pas d'édition Pro. Sans elle, les apps personnalisées n'existent pas. |
unknown | Aucune app ou aucune action de ce nom n'est enregistrée. |
too_fast | Limite de débit : au plus dix requêtes toutes les cinq secondes par joueur. |
error | Votre handler a levé une erreur. Les détails sont dans la console serveur. |
timeout | Pas de réponse en douze secondes. |
data | La requête n'était pas une table. |
Ce qui arrive dans data vient du client et reste un souhait, pas un fait. Vérifiez chaque valeur avant de l'utiliser. Tout ce qui compte (argent, objets, résultats) doit être géré sur le serveur et nulle part ailleurs.
5 Style
Les couleurs de la montre sont disponibles en variables CSS et suivent ce que le porteur a choisi dans Studio. Des couleurs codées en dur donnent une app qui reste bleue sur une montre verte.
var(--text) var(--text-2) var(--leise)
var(--akzent) var(--gruen) var(--rot) var(--gelb)
var(--karte) var(--karte-hoch) var(--linie)
var(--grund)
Préfixez vos propres classes. L'écran est partagé avec toutes les autres apps, et une classe nommée .large trouvera vite de la compagnie.
6 Icônes intégrées
Passez l'une d'elles à icon dans app.lua ou à w.icon() :
akku auto blitz brief einkauf farben flugzeug gewitter glocke haken helligkeit herz hoch hupe karte karte-pin kein-signal kino kompass kreuz krone lampe laufen lautlos links loeschen minus mobil mond nebel neuladen pause personen pin plus qr raster rechts regen regler runter sanduhr schild schloss schloss-auf schnee schritte signal sonne sp-fall sp-flug sp-ring sp-turm sprechen stoppuhr telefon telefon-aus tempo ton uhr verboten wiedergabe wlan wolke zahnrad
Les noms d'icônes restent tels quels, exprès. Ce sont des ids, pas des libellés : un joueur ne les lit jamais, et renommer un id casse toutes les apps qui l'utilisent déjà.
📦 Un exemple fonctionnel est fourni avec la montre
apps/example/ dans la resource est une app complète et exécutable : composants, appel serveur, notification et CSS propre. Copiez le dossier, renommez-le, et vous avez un point de départ qui fonctionne déjà.
Une version courte de cette page se trouve juste à côté dans apps/README.md.