export const dynamic = 'force-dynamic'; import { NextRequest, NextResponse } from "next/server"; const HA_URL = process.env.HA_URL || "http://192.168.31.110:8123"; const HA_TOKEN = process.env.HA_TOKEN || ""; const REAL_ENTITY_ALIASES: Record = { "fan.zhimi_rmb1_9528_air_purifier": "fan.air_purifier", }; const MOCK_MISSING: Record = { "light.living_room": { entity_id: "light.living_room", state: "off", attributes: { brightness: 0, friendly_name: "Свет Гостиная" }, _mock: true, }, "light.bedroom": { entity_id: "light.bedroom", state: "off", attributes: { friendly_name: "Свет Спальня" }, _mock: true, }, "climate.thermostat": { entity_id: "climate.thermostat", state: "off", attributes: { current_temperature: null, temperature: 22, friendly_name: "Термостат", }, _mock: true, }, "fan.air_purifier": { entity_id: "fan.air_purifier", state: "off", attributes: { preset_mode: "Auto", friendly_name: "Очиститель воздуха", preset_modes: ["Auto", "Night", "High"], }, _mock: true, }, }; const RELEVANT_KEYS = [ "light.living_room", "light.bedroom", "climate.thermostat", "fan.air_purifier", ]; export async function GET(req: NextRequest) { if (!HA_TOKEN) { return NextResponse.json({ demo: true, states: MOCK_MISSING }); } try { const res = await fetch(`${HA_URL}/api/states`, { headers: { Authorization: `Bearer ${HA_TOKEN}`, "Content-Type": "application/json", }, cache: "no-store", }); if (!res.ok) throw new Error(`HA responded ${res.status}`); const states: any[] = await res.json(); const filtered: Record = {}; // Get real temperature from air purifier sensor const tempSensor = states.find(s => s.entity_id === "sensor.zhimi_rmb1_9528_temperature"); const humiditySensor = states.find(s => s.entity_id === "sensor.zhimi_rmb1_9528_relative_humidity"); const pm25Sensor = states.find(s => s.entity_id === "sensor.zhimi_rmb1_9528_pm25_density"); for (const s of states) { if (RELEVANT_KEYS.includes(s.entity_id)) { filtered[s.entity_id] = s; } if (REAL_ENTITY_ALIASES[s.entity_id]) { const normalizedKey = REAL_ENTITY_ALIASES[s.entity_id]; filtered[normalizedKey] = { ...s, entity_id: normalizedKey, _real_entity_id: s.entity_id, }; } } // Fill missing with mock for (const key of RELEVANT_KEYS) { if (!filtered[key]) { filtered[key] = { ...MOCK_MISSING[key] }; } } // Inject real temperature into thermostat card if sensor exists if (tempSensor && filtered["climate.thermostat"]) { filtered["climate.thermostat"].attributes = { ...filtered["climate.thermostat"].attributes, current_temperature: parseFloat(tempSensor.state), humidity: humiditySensor ? parseFloat(humiditySensor.state) : null, pm25: pm25Sensor ? parseFloat(pm25Sensor.state) : null, }; } const hasAnyReal = RELEVANT_KEYS.some(k => !filtered[k]?._mock); return NextResponse.json({ demo: !hasAnyReal, states: filtered, sensors: { temperature: tempSensor ? parseFloat(tempSensor.state) : null, humidity: humiditySensor ? parseFloat(humiditySensor.state) : null, pm25: pm25Sensor ? parseFloat(pm25Sensor.state) : null, } }); } catch (e) { return NextResponse.json({ demo: true, states: MOCK_MISSING }); } } export async function POST(req: NextRequest) { const body = await req.json(); const { domain, service, entity_id, ...serviceData } = body; if (!HA_TOKEN) { return NextResponse.json({ success: true, demo: true }); } // Resolve alias const realEntityId = Object.keys(REAL_ENTITY_ALIASES).find( k => REAL_ENTITY_ALIASES[k] === entity_id ) || entity_id; try { const res = await fetch(`${HA_URL}/api/services/${domain}/${service}`, { method: "POST", headers: { Authorization: `Bearer ${HA_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ entity_id: realEntityId, ...serviceData }), }); if (!res.ok) { // Entity might not exist (mock) — return success anyway for local state return NextResponse.json({ success: true, mock: true }); } return NextResponse.json({ success: true }); } catch (e) { return NextResponse.json({ success: true, mock: true }); } }