using Microsoft.AspNetCore.Mvc; using GreenHome.Application; using System.Text; namespace GreenHome.Api.Controllers; [ApiController] [Route("api/[controller]")] public class TelemetryController : ControllerBase { private readonly ITelemetryService telemetryService; private readonly IAlertService alertService; private readonly IAlertConditionService alertConditionService; public TelemetryController(ITelemetryService telemetryService, IAlertService alertService, IAlertConditionService alertConditionService) { this.telemetryService = telemetryService; this.alertService = alertService; this.alertConditionService = alertConditionService; } [HttpGet] public async Task>> List([FromQuery] int? deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, [FromQuery] DateTime? sinceUtc, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) { var filter = new TelemetryFilter { DeviceId = deviceId, StartDateUtc = startUtc, EndDateUtc = endUtc, SinceUtc = sinceUtc, Page = page, PageSize = pageSize }; var result = await telemetryService.ListAsync(filter, cancellationToken); return Ok(result); } [HttpGet("AddData")] public async Task> Create([FromQuery] TelemetryDto telementry, [FromQuery] string? lastUpdateDate, CancellationToken cancellationToken) { telementry.TimestampUtc = DateTime.UtcNow; var id = await telemetryService.AddAsync(telementry, cancellationToken); var parts = new List { $"ID:{id}" }; var deviceId = telementry.DeviceId; if (deviceId > 0) { // 1. Check alerts and power status var alertResult = await alertService.CheckAndSendAlertsAsync(deviceId, telementry, cancellationToken); if (alertResult != null) { if (alertResult.Messages.Count > 0) { parts.Add($"A:{alertResult.Messages.Count}"); parts.Add($"R:{alertResult.Recipients}"); parts.Add($"M:{string.Join("@", alertResult.Messages)}"); } if (alertResult.PowerStatus != null) { parts.Add($"P:{alertResult.PowerStatus}"); } } // 2. Check relay conditions update if (!string.IsNullOrEmpty(lastUpdateDate)) { var parsedDate = ParseLastUpdateDate(lastUpdateDate); var relayConditions = await alertConditionService.GetUpdatedRelayConditionsAsync(deviceId, parsedDate, cancellationToken); if (relayConditions.Count > 0) { parts.Add("RU:1"); parts.Add($"RL:{relayConditions.Count}"); parts.Add($"RC:{EncodeRelayConditions(relayConditions)}"); } } } return Ok(string.Join("|", parts)); } /// /// Encode relay conditions into a compact string format for STM32 parsing. /// Format: E{enabled}T{timeType}R{relay}S{sensor}O{op}V{value1}[,{value2}] /// Multiple conditions separated by |, multiple rules within a condition separated by & /// private static string EncodeRelayConditions(IReadOnlyList conditions) { return string.Join("|", conditions.Select(c => { var rules = string.Join("&", c.Rules.Select(r => { var rule = $"S{r.Sensor}O{r.Op}V{r.V1}"; if (r.V2.HasValue) rule += $",{r.V2.Value}"; return rule; })); return $"E{c.Enabled}T{c.TimeType}R{c.Relay}{rules}"; })); } /// /// Parse date string in format: yyyy/MM/dd-HH:mm /// private static DateTime? ParseLastUpdateDate(string dateStr) { if (string.IsNullOrEmpty(dateStr)) return null; // Try parsing as yyyy/MM/dd-HH:mm if (DateTime.TryParseExact(dateStr, "yyyy/MM/dd-HH:mm", null, System.Globalization.DateTimeStyles.None, out var result)) { return DateTime.SpecifyKind(result, DateTimeKind.Utc); } // Try parsing as yyyy-MM-dd HH:mm (ISO-like) if (DateTime.TryParseExact(dateStr, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out var result2)) { return DateTime.SpecifyKind(result2, DateTimeKind.Utc); } return null; } [HttpGet("minmax")] public async Task> MinMax([FromQuery] int deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, CancellationToken cancellationToken) { var result = await telemetryService.GetMinMaxAsync(deviceId, startUtc, endUtc, cancellationToken); return Ok(result); } [HttpGet("days")] public async Task>> Days([FromQuery] int deviceId, [FromQuery] int year, [FromQuery] int month, CancellationToken cancellationToken) { var result = await telemetryService.GetMonthDaysAsync(deviceId, year, month, cancellationToken); return Ok(result); } [HttpGet("months")] public async Task>> Months([FromQuery] int deviceId, [FromQuery] int year, CancellationToken cancellationToken) { var result = await telemetryService.GetActiveMonthsAsync(deviceId, year, cancellationToken); return Ok(result); } [HttpGet("aggregate")] public async Task>> Aggregate([FromQuery] int deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, [FromQuery] int bucketMinutes = 15, CancellationToken cancellationToken = default) { var result = await telemetryService.GetAggregatedAsync(deviceId, startUtc, endUtc, bucketMinutes, cancellationToken); return Ok(result); } }