A Laravel backend for solar/energy device monitoring: ingests telemetry from inverters and meters through two independent paths (a vendor cloud API and a direct on-site database poll), normalises it into hourly and daily energy rollups, raises priority-scored alerts, and turns unresolved alerts into tickets — with a full project/building/device hierarchy and per-resource permissions.
This is a generalised reference implementation of a production system. The real folder structure, ingestion pipelines, rollup strategy and alerting model are preserved; employer, vendor and site names are replaced with neutral ones, and the database dump plus every credential are removed.
- Two independent ingestion paths for the same data — a vendor cloud API
(
VendorApiDeviceSync,VendorApiHistoricalDataSync,VendorApiAlertSync) and a direct poll of an on-site database (HeliosDbPollCommand,HeliosMeterDbPollCommand). Sites differ in what they expose, and a cloud API that goes down must not stop data collection from a site that can be read directly. - Backlog replay as a first-class operation —
ReplayTelemetryFeedBacklogandProcessTelemetryFeedJsonFilesCommandreprocess raw payloads that arrived while the pipeline was down. Device telemetry cannot be re-requested after the fact, so raw payloads are archived (ArchiveTelemetryFeedRawFiles) before parsing and can be replayed. - Rollups are rebuildable, not append-only —
RebuildDailyDeviceSummariesandTelemetryFeedRecomputeDailyEnergyrecomputeDailyDeviceSummary/DailyProjectSummary/HourlyDevicePowerGenerationfrom source records. Late-arriving or corrected telemetry is normal in device fleets, so a summary that can only be appended to is permanently wrong the first time a device backfills. - Alert priority as configuration —
AlertPriorityMapping+AlertPriorityMappingService, withBackfillAlertPrioritiesto reapply a changed mapping to alerts already raised. - Alerts become tickets automatically —
CreateMissingTicketsreconciles alerts that should have produced a ticket and did not, so a failure in the alert→ticket path is self-healing rather than silently losing work. - Live power cached separately from history —
TelemetryFeedLivePowerCacheServiceserves dashboard "current output" without touching the historical tables the rollups are built from. - Resource-scoped permissions —
UserResourcePermissionalongsideRole/Permission, so access is granted per project/building/device rather than globally. - Internal code generators —
MakeCrudCommandandMakeServiceCommandwithstubs/, keeping new resources structurally consistent. - Error paths are recorded —
DeviceGenerationErrorLogandTelemetryFeedPayloadLoggerkeep what failed to parse, so a malformed payload is diagnosable instead of just dropped.
app/
Console/Commands/
VendorApi{Device,Project,Alert,HistoricalData}Sync.php cloud ingestion
HeliosDbPollCommand.php · HeliosMeterDbPollCommand.php on-site DB polling
ProcessTelemetryFeedJsonFilesCommand.php raw payload parsing
ReplayTelemetryFeedBacklog.php gap recovery
ArchiveTelemetryFeedRawFiles.php raw retention
RebuildDailyDeviceSummaries.php
TelemetryFeedRecomputeDailyEnergy.php rollup rebuilds
BackfillAlertPriorities.php · CreateMissingTickets.php reconciliation
MakeCrudCommand.php · MakeServiceCommand.php scaffolding
Jobs/ vendor API fetch/parse/import, alert processing,
ticket notification
Services/ Alert, AlertPriorityMapping, Device, Project, Building,
Entity, Tariff, Ticket, Dashboard, Permission, Role,
User, FCM, Sms, TelemetryFeedLivePowerCache,
TelemetryFeedPayloadLogger, Apis/
Models/ Device · DeviceGeneration(+ErrorLog)
HourlyDevicePowerGeneration
DailyDeviceSummary · DailyProjectSummary
Project · Building · Entity · Tariff
Alert · AlertPriorityMapping
Ticket · TicketComment · TicketHistory
Role · Permission · UserResourcePermission
config/ database/ routes/ stubs/ supervisors/ documentation/ tests/
cp .env.example .env # DB, vendor API, on-site DB, FCM, SMS
composer install
php artisan migrate
php artisan schedule:work # drives polling, rollups and reconciliation
php artisan queue:workShips without vendor/, without any database dump, and with every credential
read from the environment.