Voor mijn eigen brandweerpost heb ik een applicatie gebouwd waarmee diensten en oefeningen worden ingepland, en waarmee leden via Telegram op de hoogte blijven. Wat begon als “even een rooster maken” is uitgegroeid tot een systeem met een verrassend genuanceerd indelingsalgoritme erachter — inmiddels met een derde fase: een eigen mobiele app onder de naam BrandweerPlanning.
Hoe de indeling werkt
Rollen worden gegroepeerd in door de beheerder zelf gedefinieerde rolgroepen (kolommen op het overzicht, zoals “Uitzetploeg” of “Speelploeg”), elk met een eigen vulvolgorde. Per rol is in te stellen of hij verplicht gevuld moet worden, of er een specifieke opleiding voor nodig is, en of hij altijd handmatig wordt toegewezen (bijvoorbeeld de oefenleider) — andere rollen kunnen dan pas automatisch vullen zodra die handmatige rollen zijn ingevuld, zodat de rest van de indeling logisch aansluit op wie de leiding heeft.
Een groep kan ook als “voorkeurgroep” worden gemarkeerd: rollen daarbinnen zijn dan óf de leuke rol óf de minder leuke rol. De app onthoudt per persoon hoe vaak diegene op rij de niet-voorkeursrol heeft gekregen, en geeft die persoon dan voorrang op de voorkeursrol. Bij de eerlijkheid zelf wordt op twee niveaus gekeken: eerst hoe vaak iemand in de hele roosterperiode een rol heeft vervuld die dezelfde opleiding vereist als de open rol (brede eerlijkheid), en pas daarna, als tiebreaker, hoe vaak diegene precies déze rol al deed. Wat overblijft wordt willekeurig beslist.
Ook late wijzigingen zijn ondervangen: meldt iemand zich binnen twee dagen voor een oefening af, dan schuift het systeem automatisch iemand uit de overflow-rol door naar de vrijgekomen plek, met dezelfde eerlijkheidslogica. Meldt iemand zich juist weer aan, dan wordt gekeken of er nog een passende open rol is, en anders belandt diegene in de overflow. Per rooster is bovendien in te stellen wanneer een oefening automatisch “definitief” wordt (bijvoorbeeld twee dagen van tevoren om 18:00) — een achtergrondservice bewaakt dat moment en sluit de indeling dan vanzelf.
De Telegram-bot
In de tweede fase kregen leden toegang via Telegram, zonder aparte app of account. Die route blijft bestaan naast de nieuwe mobiele app. De bot laat je aan- en afmelden voor oefeningen, toont de indeling van de week, geeft je eigen statistieken (of die van een postgenoot), laat zien wie er bij jouw post horen, en begeleidt nieuwe leden door een aanmeldproces. Alles werkt met knoppen in de chat, en een beheerder kan zelfs vanuit een chatbericht rollen toewijzen of oefendoelen aan- en uitvinken. Nieuwe aanmeldingen komen in een wachtrij totdat een beheerder ze koppelt aan een bestaand persoon.
De beheer-omgeving
Daarnaast is er een admin-omgeving voor posten, personen, opleidingen, roosters, rolvervulling en oefendoelen-statistieken, met Telegram-koppelingen beheren, aanmeldingen goed- of afkeuren en een testbericht-functie om een koppeling te checken.
Versie 1 versus versie 2
De basis voor de huidige app is in twee eerdere fasen gelegd. De eerste opzet (najaar 2025) had een vaste, hardgecodeerde lijst van dertien rollen die niet per post aan te passen was, en het indelingsalgoritme kwam neer op: heeft iemand de juiste opleiding, en zo ja, wie heeft deze specifieke rol tot nu toe het minst vaak gedaan — daarna gewoon willekeurig. Geen rolgroepen, geen voorkeurslogica, geen brede eerlijkheid over opleidingen heen, geen opvang van late af- of aanmeldingen.
Daarna is het project een half jaar blijven liggen: van januari tot eind juli 2026 gebeurde er niets. Toen ik het weer oppakte, is in een paar dagen tijd vrijwel alles herbouwd: de Telegram-bot kwam er in één keer bij, en kort daarna volgden razendsnel achter elkaar de configureerbare rolgroepen, de voorkeurslogica, opleiding-brede eerlijkheid, automatische herindeling bij afmeldingen, automatisch definitief maken van oefeningen, statistieken per rol en oefendoel, en de hele beheer-omgeving eromheen. Wat in versie 1 een simpel schema was, is in versie 2 een systeem geworden dat continu leert van wie wat al heeft gedaan.
De technische basis van fase 2
De applicatie bestaat uit een gelaagde .NET 8-backend (Domain, Application, Persistence, Infrastructure, WebApi) met PostgreSQL via Entity Framework Core, en een Next.js/React-frontend met shadcn/ui.
De infrastructuur staat volledig als code in Bicep, en het leukste daaraan is dat er nergens een wachtwoord voorkomt:
- Azure App Service (Linux, gedeeld App Service Plan) host zowel de backend (.NET 8) als de frontend (Node 20, via pm2), elk met een system-assigned managed identity.
- Azure Database for PostgreSQL Flexible Server draait met wachtwoord-authenticatie volledig uitgeschakeld — alleen inloggen via Entra ID is mogelijk. Een user-assigned managed identity is ingesteld als Postgres AAD-beheerder, en een deployment-script gebruikt die identity om de managed identity van de backend als niet-admin databaserol toe te voegen. De backend logt vervolgens in met een Entra-token dat elke paar minuten ververst wordt via DefaultAzureCredential — er bestaat dus letterlijk geen databasewachtwoord om te lekken.
- Azure Key Vault (RBAC-geautoriseerd, geen access policies) bewaart de paar secrets die nog geen Entra-tegenhanger hebben: het Auth0 client secret, de Auth0 sessie-cookie secret, en het Telegram bot-token. Beide Web Apps lezen die via Key Vault-references in hun app settings, zodat de waarden nooit los in configuratie belanden.
- Azure DevOps Pipelines deployt alles, met een service connection op basis van workload identity federation (OIDC) in plaats van een service principal-secret — ook de CI/CD-keten zelf werkt dus zonder rondslingerende sleutels.
- Alles is geparametriseerd op projectnaam en omgevingsnaam, zodat dezelfde Bicep zonder aanpassing naar een andere omgeving of subscription kan worden uitgerold.
In fase 2 liepen authenticatie en autorisatie nog via Auth0. Inmiddels gebruikt het webbeheer Microsoft Entra ID; voor de mobiele app is daar een afzonderlijke apparaatkoppeling bij gekomen.
Fase 3: BrandweerPlanning als eigen app
De volgende stap is een eigen app voor iOS en Android: BrandweerPlanning. De bestaande planner blijft de basis, maar vrijwilligers hoeven voor hun dagelijkse overzicht niet meer via een Telegram-gesprek te werken. Home, Rooster, Meldingen en Profiel brengen de belangrijkste informatie bij elkaar, in een donkere interface die ook op een iPad ruimte krijgt.
Je rooster, meldingen en profiel bij de hand
Het rooster groepeert komende bijeenkomsten per maand en biedt een aparte terugblik op eerdere oefeningen. Vanuit Home of Rooster open je de details of geef je direct je beschikbaarheid door. Bij een bijeenkomst bekijk je de indeling per ploeg en de oefendoelen. In Profiel vind je je opleidingen, je posten en het overzicht van je inzet.
Meldingen hebben een eigen tabblad. Een gepubliceerde indeling, een gewijzigde rol of verwerkte beschikbaarheid kan zo ook in de app terugkomen. Pushmeldingen zijn optioneel: zonder toestemming voor push blijven de berichten in de app leesbaar. Een achtergrondproces volgt hiervoor ook wijzigingen die via het webbeheer, Telegram of de automatische planner zijn gedaan.
Koppelen met goedkeuring van de beheerder
Je koppelt je apparaat met een QR-code of invoercode van de beheerder. Daarna bevestigt de beheerder welke bestaande persoon bij het apparaat hoort. Pas na goedkeuring krijg je toegang tot je eigen roosters. Het webbeheer heeft hiervoor een aparte pagina om aanvragen te beoordelen en apparaten weer te ontkoppelen.
Ook dagelijks oefenbeheer is in de app opgenomen. Afhankelijk van je bestaande bevoegdheden kun je bijvoorbeeld rollen toewijzen, oefendoelen aanpassen en een indeling publiceren. De backend blijft bepalen wie wat mag; de mobiele app gebruikt dezelfde planningsregels als de bestaande omgeving.
Van telefoon naar tablet
De schermen passen zich aan de beschikbare ruimte aan. Een roosterbeheerder kan bovendien een gedeelde roosterachtergrond kiezen: de meegeleverde brandweerfoto, een eigen foto of effen donker. Eigen foto's worden verkleind en via de API in privé Azure Blob Storage opgeslagen.
Bekijk Rooster, Meldingen en Profiel op iPad
De stap richting App Store en Google Play
De mobiele app is gebouwd met Expo, React Native en TypeScript, met één codebasis voor iOS en Android en ondersteuning voor tablets. Daarvoor zijn onder meer een eigen app-icoon, platformidentifiers, cameratoegang voor QR-codes, fotoselectie en pushmeldingen toegevoegd. EAS bevat afzonderlijke buildprofielen voor ontwikkeling, interne previews en productie, met automatische ophoging van het buildnummer voor productie.
Achter de schermen is er een aparte mobiele API bij gekomen. Apparaatcredentials staan op de telefoon in SecureStore; de server controleert bij iedere aanvraag of de koppeling nog actief is. Eerder opgehaalde gegevens worden tijdelijk lokaal bewaard. Voor push houdt de backend afleverpogingen en ontvangstbevestigingen bij, zodat mislukte afleveringen opnieuw geprobeerd kunnen worden.
Die technische voorbereiding is de basis voor distributie via de Apple App Store en Google Play Store. Ondertekening, store-inrichting, testen op echte apparaten en de beoordeling door de stores horen bij het uitbrengen van de app.
Hulp nodig bij koppelen of gebruiken? Op de supportpagina voor BrandweerPlanning staan de stappen, veelgestelde vragen en contactmogelijkheden.
