false, 'error' => 'Invalid JSON body', ), 400 ); } return $payload; } function requireBridgeToken( string $expectedToken ): void { $providedToken = $_SERVER['HTTP_X_BRIDGE_TOKEN'] ?? ''; if ( ! hash_equals( $expectedToken, $providedToken ) ) { sendJson( array( 'ok' => false, 'error' => 'Invalid bridge token', ), 401 ); } } function validateReading( array $payload ): void { $requiredFields = array( 'deviceId', 'connected', 'battery', 'unit', 'probes', 'readingTime', ); foreach ( $requiredFields as $field ) { if ( ! array_key_exists( $field, $payload ) ) { sendJson( array( 'ok' => false, 'error' => 'Missing required field: ' . $field, ), 422 ); } } if ( ! is_array( $payload['probes'] ) ) { sendJson( array( 'ok' => false, 'error' => 'The probes field must be an array', ), 422 ); } } $path = getRequestPath(); $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; if ( $method === 'POST' && $path === '/api/thermopro/readings' ) { requireBridgeToken( $token ); $payload = readJsonBody(); validateReading( $payload ); $payload['serverReceivedAt'] = gmdate( DATE_ATOM ); file_put_contents( $latestPath, json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) ); file_put_contents( $logPath, json_encode( $payload, JSON_UNESCAPED_SLASHES ) . PHP_EOL, FILE_APPEND | LOCK_EX ); sendJson( array( 'ok' => true, 'receivedAt' => $payload['serverReceivedAt'], ) ); } if ( $method === 'GET' && $path === '/api/thermopro/latest' ) { if ( ! file_exists( $latestPath ) ) { sendJson( array( 'ok' => false, 'error' => 'No readings received yet', ), 404 ); } header( 'Content-Type: application/json' ); readfile( $latestPath ); exit; } if ( $method === 'GET' && ( $path === '/' || $path === '/status' ) ) { $latest = null; if ( file_exists( $latestPath ) ) { $latest = json_decode( file_get_contents( $latestPath ), true ); } header( 'Content-Type: text/html; charset=utf-8' ); ?>
No readings received yet.
Latest reading received at:
false, 'error' => 'Not found', ), 404 );