156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
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<string, string> = {
|
|
"fan.zhimi_rmb1_9528_air_purifier": "fan.air_purifier",
|
|
};
|
|
|
|
const MOCK_MISSING: Record<string, any> = {
|
|
"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<string, any> = {};
|
|
|
|
// 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 });
|
|
}
|
|
}
|