header('X-Netgescon-Water-Token') ?: $request->bearerToken() ?: $request->query('token', ''))); if (! hash_equals($expectedToken, $provided)) { return response()->json(['ok' => false, 'message' => 'Token gateway non valido'], 403); } } elseif (! app()->environment(['local', 'testing'])) { return response()->json(['ok' => false, 'message' => 'Token gateway acqua non configurato'], 503); } $data = $request->validate([ 'device_id' => ['nullable', 'string', 'max:120'], 'serial' => ['nullable', 'string', 'max:100'], 'unita_id' => ['nullable', 'integer'], 'value' => ['required', 'numeric'], 'read_at' => ['nullable', 'date'], 'unit' => ['nullable', 'string', 'max:20'], 'battery' => ['nullable', 'numeric'], 'rssi' => ['nullable', 'numeric'], 'payload' => ['nullable', 'array'], ]); $unita = $this->resolveUnita($data); if (! $unita instanceof UnitaImmobiliare) { return response()->json(['ok' => false, 'message' => 'Unità non trovata per device_id/seriale'], 404); } $servizio = StabileServizio::query() ->where('stabile_id', (int) $unita->stabile_id) ->where('tipo', 'acqua') ->where('attivo', true) ->orderBy('id') ->first(); if (! $servizio instanceof StabileServizio) { $servizio = StabileServizio::query()->create([ 'stabile_id' => (int) $unita->stabile_id, 'tipo' => 'acqua', 'nome' => 'Acqua - gateway contatori', 'contatore_matricola' => trim((string) ($data['serial'] ?? '')) ?: null, 'attivo' => true, 'meta' => ['source' => 'water_gateway_auto'], ]); } $readAt = isset($data['read_at']) && $data['read_at'] ? Carbon::parse($data['read_at']) : now(); $value = round((float) $data['value'], 3); $previous = StabileServizioLettura::query() ->where('unita_immobiliare_id', (int) $unita->id) ->where('stabile_servizio_id', (int) $servizio->id) ->whereNotNull('lettura_fine') ->orderByDesc('periodo_al') ->orderByDesc('id') ->first(); $previousValue = $previous instanceof StabileServizioLettura ? (float) $previous->lettura_fine : null; $consumption = $previousValue !== null ? max(0, round($value - $previousValue, 3)) : 0.0; $reading = StabileServizioLettura::query()->create([ 'stabile_id' => (int) $unita->stabile_id, 'stabile_servizio_id' => (int) $servizio->id, 'unita_immobiliare_id' => (int) $unita->id, 'periodo_dal' => $previous?->periodo_al ?: $readAt->toDateString(), 'periodo_al' => $readAt->toDateString(), 'tipologia_lettura' => 'reale', 'canale_acquisizione' => 'gateway', 'riferimento_acquisizione' => trim((string) ($data['device_id'] ?? $data['serial'] ?? 'gateway')), 'workflow_stato' => 'acquisita', 'rilevatore_tipo' => 'gateway_iot', 'rilevatore_nome' => trim((string) ($data['device_id'] ?? 'gateway')), 'lettura_precedente_valore' => $previousValue, 'lettura_inizio' => $previousValue, 'lettura_fine' => $value, 'consumo_valore' => $consumption, 'consumo_unita' => trim((string) ($data['unit'] ?? 'mc')) ?: 'mc', 'raw' => [ 'source' => 'water_meter_gateway', 'device_id' => $data['device_id'] ?? null, 'serial' => $data['serial'] ?? null, 'battery' => $data['battery'] ?? null, 'rssi' => $data['rssi'] ?? null, 'payload' => $data['payload'] ?? [], ], ]); $this->updateUnitGatewayFields($unita, $data); return response()->json([ 'ok' => true, 'reading_id' => (int) $reading->id, 'unita_id' => (int) $unita->id, 'stabile_id' => (int) $unita->stabile_id, 'stabile_servizio_id' => (int) $servizio->id, 'lettura_fine' => $value, 'consumo' => $consumption, ]); } /** @param array $data */ private function resolveUnita(array $data): ?UnitaImmobiliare { $query = UnitaImmobiliare::query(); if (Schema::hasColumn('unita_immobiliari', 'deleted_at')) { $query->whereNull('deleted_at'); } if (! empty($data['unita_id'])) { $found = (clone $query)->whereKey((int) $data['unita_id'])->first(); if ($found instanceof UnitaImmobiliare) { return $found; } } $deviceId = trim((string) ($data['device_id'] ?? '')); if ($deviceId !== '' && Schema::hasColumn('unita_immobiliari', 'acqua_gateway_device_id')) { $found = (clone $query)->where('acqua_gateway_device_id', $deviceId)->first(); if ($found instanceof UnitaImmobiliare) { return $found; } } $serial = trim((string) ($data['serial'] ?? '')); if ($serial !== '' && Schema::hasColumn('unita_immobiliari', 'acqua_contatore_seriale')) { $found = (clone $query)->where('acqua_contatore_seriale', $serial)->first(); if ($found instanceof UnitaImmobiliare) { return $found; } } return null; } /** @param array $data */ private function updateUnitGatewayFields(UnitaImmobiliare $unita, array $data): void { $patch = []; if (empty($unita->acqua_gateway_device_id) && ! empty($data['device_id'])) { $patch['acqua_gateway_device_id'] = trim((string) $data['device_id']); } if (empty($unita->acqua_contatore_seriale) && ! empty($data['serial'])) { $patch['acqua_contatore_seriale'] = trim((string) $data['serial']); } if (Schema::hasColumn('unita_immobiliari', 'acqua_gateway_meta')) { $patch['acqua_gateway_meta'] = array_filter([ 'last_seen_at' => now()->toIso8601String(), 'battery' => $data['battery'] ?? null, 'rssi' => $data['rssi'] ?? null, ], static fn($value): bool => $value !== null); } if ($patch !== []) { $unita->fill($patch); $unita->save(); } } }