'use client' import { useState, useEffect, useCallback } from 'react' import { Thermometer, Droplets, Wind, Calendar, Sun, CloudRain, Snowflake as SnowIcon, Cloud, CloudSun, Zap, Settings as SettingsIcon } from 'lucide-react' import Sidebar from '@/components/Sidebar' import TopBar from '@/components/TopBar' import RoomTabs from '@/components/RoomTabs' import DeviceCard from '@/components/DeviceCard' import CalendarTab from '@/components/CalendarTab' type Tab = 'home' | 'devices' | 'calendar' | 'settings' interface WeatherData { temp: string desc: string humidity: string windSpeed: string feelsLike: string forecast?: { date: string; maxTemp: string; minTemp: string; desc: string }[] } interface SensorData { temperature: number humidity: number pm25: number } interface HaStates { [key: string]: { state: string; attributes?: Record; _mock?: boolean } } interface CalendarEvent { id: string title: string start: string end: string allDay: boolean owner: string ownerName: string color: string } const ROOMS = [ { id: 'living', name: 'Гостиная', emoji: '🛋️', deviceCount: 3 }, { id: 'bedroom', name: 'Спальня', emoji: '🛏️', deviceCount: 2 }, { id: 'kitchen', name: 'Кухня', emoji: '🍳', deviceCount: 0 }, { id: 'bathroom', name: 'Ванная', emoji: '🚿', deviceCount: 0 }, ] const DEVICES_BY_ROOM: Record = { living: [ { id: 'air_purifier', name: 'Очиститель воздуха', icon: '💨', entityId: 'fan.zhimi_rmb1_9528_air_purifier', domain: 'fan', haKey: 'fan.air_purifier', isMock: false }, { id: 'light_living', name: 'Свет', icon: '💡', entityId: 'light.living_room', domain: 'light', haKey: 'light.living_room', isMock: true }, { id: 'tv', name: 'Телевизор', icon: '📺', isMock: true }, ], bedroom: [ { id: 'light_bedroom', name: 'Свет', icon: '💡', entityId: 'light.bedroom', domain: 'light', haKey: 'light.bedroom', isMock: true }, { id: 'ac', name: 'Кондиционер', icon: '❄️', isMock: true }, ], kitchen: [], bathroom: [], } function getWeatherIcon(desc: string): string { const d = desc?.toLowerCase() || '' if (d.includes('ясно') || d.includes('солнеч')) return '☀️' if (d.includes('облач')) return '⛅' if (d.includes('пасмурн')) return '☁️' if (d.includes('дождь') || d.includes('морос')) return '🌧️' if (d.includes('снег')) return '❄️' if (d.includes('гроз')) return '⛈️' return '🌤️' } function formatEventTime(iso: string): string { const d = new Date(iso) return d.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }) } function getPm25Level(pm25: number): { label: string; color: string; bg: string } { if (pm25 <= 12) return { label: 'Отлично', color: '#34d399', bg: 'rgba(52,211,153,0.12)' } if (pm25 <= 35) return { label: 'Хорошо', color: '#a3e635', bg: 'rgba(163,230,53,0.12)' } if (pm25 <= 55) return { label: 'Умеренно', color: '#fbbf24', bg: 'rgba(251,191,36,0.12)' } return { label: 'Плохо', color: '#f87171', bg: 'rgba(248,113,113,0.12)' } } // ————— Home Tab ————— function HomeTab({ weather, sensors }: { weather: WeatherData | null; sensors: SensorData | null }) { const [todayEvents, setTodayEvents] = useState([]) const [calLoading, setCalLoading] = useState(true) useEffect(() => { fetch('/api/calendar?range=today') .then(r => r.json()) .then(d => setTodayEvents(d.events || [])) .catch(() => setTodayEvents([])) .finally(() => setCalLoading(false)) }, []) const pm25Info = sensors ? getPm25Level(sensors.pm25) : null return (
{/* Top row: Weather + Sensors side by side */}
{/* Weather Card */} {weather && (
{/* Background decoration */}
{getWeatherIcon(weather.desc)}
Погода
{getWeatherIcon(weather.desc)}
{weather.temp}°
{weather.desc}
{/* Forecast mini */} {weather.forecast && weather.forecast.length > 0 && (
{weather.forecast.slice(0, 3).map(day => { const d = new Date(day.date) const label = d.toLocaleDateString('ru-RU', { weekday: 'short' }) return (
{label}
{getWeatherIcon(day.desc)}
{day.maxTemp}°
{day.minTemp}°
) })}
)}
)} {/* Sensors Card */} {sensors && (
Климат в квартире
{/* Temperature */}
{sensors.temperature}°C
Температура
{/* Humidity */}
{sensors.humidity}%
Влажность
{/* PM2.5 */}
{sensors.pm25} µg/m³
PM2.5 · {pm25Info?.label}
)}
{/* Today Events */}
Сегодня
{calLoading ? (
Загрузка...
) : todayEvents.length === 0 ? (
Нет событий на сегодня
) : (
{todayEvents.map(ev => (
{ev.title}
{ev.allDay ? 'Весь день' : `${formatEventTime(ev.start)} — ${formatEventTime(ev.end)}`} {ev.ownerName}
))}
)}
) } export default function HomePage() { const [tab, setTab] = useState('home') const [activeRoom, setActiveRoom] = useState('living') const [weather, setWeather] = useState(null) const [sensors, setSensors] = useState(null) const [haStates, setHaStates] = useState({}) useEffect(() => { const load = async () => { try { const r = await fetch('/api/weather') const d = await r.json() if (d.temp && d.temp !== '—') setWeather(d) } catch {} } load() const t = setInterval(load, 600_000) return () => clearInterval(t) }, []) const loadHA = useCallback(async () => { try { const r = await fetch('/api/ha') const d = await r.json() if (d.states) setHaStates(d.states) if (d.sensors) setSensors(d.sensors) } catch {} }, []) useEffect(() => { loadHA() const t = setInterval(loadHA, 30_000) return () => clearInterval(t) }, [loadHA]) const devicesInRoom = DEVICES_BY_ROOM[activeRoom] || [] const getDeviceState = (haKey?: string): boolean => { if (!haKey || !haStates[haKey]) return false return haStates[haKey].state === 'on' } const getDeviceExtra = (id: string): string | undefined => { if (id === 'air_purifier' && sensors) return `PM2.5: ${sensors.pm25}` return undefined } return (
{/* Ambient background */}
{tab === 'home' && } {tab === 'devices' && ( <>
{devicesInRoom.length === 0 ? (
🏠
Устройства не добавлены
) : (
{devicesInRoom.map(device => ( ))}
)}
)} {tab === 'calendar' && } {tab === 'settings' && (
Настройки Скоро
)}
) }