feat: initial smart home dashboard
All checks were successful
Deploy to Coolify / deploy (push) Successful in 44s
All checks were successful
Deploy to Coolify / deploy (push) Successful in 44s
- Next.js 14 + TypeScript + Tailwind CSS - Glassmorphism design with ambient orbs - Cards: Light x2, Temperature, AirPurifier, Tasks, Weather, Savings - Home Assistant integration (demo mode if no token) - Vikunja tasks API - Pulse savings API - wttr.in weather - Framer Motion animations - Dark/light theme toggle - Bottom navigation - Dockerfile for deployment
This commit is contained in:
66
lib/api.ts
Normal file
66
lib/api.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
export async function callHA(
|
||||
domain: string,
|
||||
service: string,
|
||||
entity_id: string,
|
||||
extra?: Record<string, any>
|
||||
) {
|
||||
const res = await fetch("/api/ha", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ domain, service, entity_id, ...extra }),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function toggleLight(entity_id: string, on: boolean) {
|
||||
return callHA("light", on ? "turn_on" : "turn_off", entity_id);
|
||||
}
|
||||
|
||||
export async function setLightBrightness(entity_id: string, brightness: number) {
|
||||
return callHA("light", "turn_on", entity_id, { brightness });
|
||||
}
|
||||
|
||||
export async function toggleFan(entity_id: string, on: boolean) {
|
||||
return callHA("fan", on ? "turn_on" : "turn_off", entity_id);
|
||||
}
|
||||
|
||||
export async function setFanPreset(entity_id: string, preset_mode: string) {
|
||||
return callHA("fan", "set_preset_mode", entity_id, { preset_mode });
|
||||
}
|
||||
|
||||
export async function setClimateTemp(entity_id: string, temperature: number) {
|
||||
return callHA("climate", "set_temperature", entity_id, { temperature });
|
||||
}
|
||||
|
||||
export async function fetchWeather() {
|
||||
const res = await fetch("/api/weather", { next: { revalidate: 600 } });
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchTasks() {
|
||||
const res = await fetch("/api/tasks", { cache: "no-store" });
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function createTask(title: string) {
|
||||
const res = await fetch("/api/tasks", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title }),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function toggleTask(id: number, done: boolean) {
|
||||
const res = await fetch("/api/tasks", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id, done }),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchSavings() {
|
||||
const res = await fetch("/api/savings", { next: { revalidate: 300 } });
|
||||
return res.json();
|
||||
}
|
||||
19
lib/ha.ts
Normal file
19
lib/ha.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface HAState {
|
||||
entity_id: string;
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface HAStates {
|
||||
demo: boolean;
|
||||
states: Record<string, HAState>;
|
||||
}
|
||||
|
||||
export function getBrightnessPct(brightness?: number): number {
|
||||
if (!brightness) return 0;
|
||||
return Math.round((brightness / 255) * 100);
|
||||
}
|
||||
|
||||
export function pctToBrightness(pct: number): number {
|
||||
return Math.round((pct / 100) * 255);
|
||||
}
|
||||
Reference in New Issue
Block a user