null, 'trend' => null, 'min' => null, 'min_at' => null, 'max' => null, 'max_at' => null, 'humidity' => null]; private const EMPTY_PRESSURE = ['current' => null, 'trend' => null]; /** * @param array $body * @param array $indoorModules _id => label, in de gewenste volgorde * @return array{timezone: ?string, lat: ?float, lon: ?float, device_id: ?string, measured_at: ?int, * outdoor: array{temp: ?float, trend: ?string, min: ?float, min_at: ?int, max: ?float, max_at: ?int, humidity: ?float}, * indoor: list, * pressure: array{current: ?float, trend: ?string}, * outdoor_module_id: ?string, rain_module_id: ?string, sum_rain_24: ?float} */ public static function map(array $body, ?string $deviceId, array $indoorModules): array { $device = self::pickDevice($body['devices'] ?? [], $deviceId); if ($device === null) { return [ 'timezone' => null, 'lat' => null, 'lon' => null, 'device_id' => null, 'measured_at' => null, 'outdoor' => self::EMPTY_OUTDOOR, 'indoor' => [], 'pressure' => self::EMPTY_PRESSURE, 'outdoor_module_id' => null, 'rain_module_id' => null, 'sum_rain_24' => null, ]; } $modules = is_array($device['modules'] ?? null) ? $device['modules'] : []; $outdoor = self::firstOfType($modules, 'NAModule1'); $rain = self::firstOfType($modules, 'NAModule3'); $location = $device['place']['location'] ?? null; return [ 'timezone' => self::stringOrNull($device['place']['timezone'] ?? null), 'lat' => is_array($location) && isset($location[1]) ? (float) $location[1] : null, 'lon' => is_array($location) && isset($location[0]) ? (float) $location[0] : null, 'device_id' => self::stringOrNull($device['_id'] ?? null), 'measured_at' => self::intOrNull(self::dashboard($device)['time_utc'] ?? self::dashboard($outdoor)['time_utc'] ?? null), 'outdoor' => self::mapOutdoor($outdoor), 'indoor' => self::mapIndoor($device, $modules, $indoorModules), 'pressure' => [ 'current' => self::floatOrNull(self::dashboard($device)['Pressure'] ?? null), 'trend' => self::stringOrNull(self::dashboard($device)['pressure_trend'] ?? null), ], 'outdoor_module_id' => $outdoor === null ? null : self::stringOrNull($outdoor['_id'] ?? null), 'rain_module_id' => $rain === null ? null : self::stringOrNull($rain['_id'] ?? null), 'sum_rain_24' => self::floatOrNull(self::dashboard($rain)['sum_rain_24'] ?? null), ]; } /** @param list> $devices */ private static function pickDevice(array $devices, ?string $deviceId): ?array { foreach ($devices as $device) { if ($deviceId !== null && ($device['_id'] ?? null) === $deviceId) { return $device; } } return $devices[0] ?? null; } /** @param list> $modules */ private static function firstOfType(array $modules, string $type): ?array { foreach ($modules as $module) { if (($module['type'] ?? null) === $type) { return $module; } } return null; } /** Dashboard-data van een module, of leeg als de module onbereikbaar is. */ private static function dashboard(?array $module): array { if ($module === null || ($module['reachable'] ?? true) === false) { return []; } return is_array($module['dashboard_data'] ?? null) ? $module['dashboard_data'] : []; } private static function mapOutdoor(?array $module): array { $d = self::dashboard($module); if ($d === []) { return self::EMPTY_OUTDOOR; } return [ 'temp' => self::floatOrNull($d['Temperature'] ?? null), 'trend' => self::stringOrNull($d['temp_trend'] ?? null), 'min' => self::floatOrNull($d['min_temp'] ?? null), 'min_at' => self::intOrNull($d['date_min_temp'] ?? null), 'max' => self::floatOrNull($d['max_temp'] ?? null), 'max_at' => self::intOrNull($d['date_max_temp'] ?? null), 'humidity' => self::floatOrNull($d['Humidity'] ?? null), ]; } /** * @param list> $modules * @param array $indoorModules */ private static function mapIndoor(array $device, array $modules, array $indoorModules): array { $candidates = []; foreach (array_merge([$device], $modules) as $module) { $type = $module['type'] ?? null; if ($type !== 'NAMain' && $type !== 'NAModule4') { continue; } $id = self::stringOrNull($module['_id'] ?? null); if ($id === null) { continue; } $d = self::dashboard($module); $candidates[$id] = [ 'id' => $id, 'name' => $indoorModules[$id] ?? self::stringOrNull($module['module_name'] ?? null) ?? $id, 'temp' => self::floatOrNull($d['Temperature'] ?? null), 'min' => self::floatOrNull($d['min_temp'] ?? null), 'max' => self::floatOrNull($d['max_temp'] ?? null), 'humidity' => self::floatOrNull($d['Humidity'] ?? null), 'co2' => self::intOrNull($d['CO2'] ?? null), 'reachable' => $d !== [], ]; } $ordered = []; foreach (array_keys($indoorModules) as $id) { if (isset($candidates[$id])) { $ordered[] = $candidates[$id]; unset($candidates[$id]); } } return array_merge($ordered, array_values($candidates)); } private static function floatOrNull(mixed $v): ?float { return is_int($v) || is_float($v) ? (float) $v : null; } private static function intOrNull(mixed $v): ?int { return is_int($v) || is_float($v) ? (int) $v : null; } private static function stringOrNull(mixed $v): ?string { return is_string($v) && $v !== '' ? $v : null; } }