Files
ems/frontend/src/api/backend.ts
Dusan Vojacek 9f4126946d second version
2026-04-03 14:23:16 +02:00

287 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import axios, { type AxiosInstance } from 'axios'
import type { FullStatusResponse } from '../types/fullStatus'
import type { Notification } from '../types/dashboard'
import type { CurrentPlanResponse, RunPlanResponse } from '../types/plan'
const client: AxiosInstance = axios.create({
baseURL: '/api/v1',
headers: { Accept: 'application/json' },
timeout: 30_000,
})
/** Příklad: health / readiness až budou v FastAPI exponované. */
export async function getBackendHealth(): Promise<unknown> {
const { data } = await client.get('/health')
return data
}
export type HealthDetailedResponse = {
db: 'ok' | 'error'
scheduler: 'running' | 'stopped'
telemetry_loop: 'running' | 'stopped'
last_telemetry_age_sec: number
last_plan_age_sec: number
active_jobs: { id: string; next_run_time: string | null }[]
}
export async function getBackendHealthDetailed(): Promise<HealthDetailedResponse> {
const { data } = await client.get<HealthDetailedResponse>('/health/detailed')
return data
}
export async function getSiteStatusFull(siteId: number): Promise<FullStatusResponse> {
const { data } = await client.get<FullStatusResponse>(`/sites/${siteId}/status/full`)
return data
}
export type SiteNotificationsResponse = {
notifications: Notification[]
}
export async function getSiteNotifications(siteId: number): Promise<SiteNotificationsResponse> {
const { data } = await client.get<SiteNotificationsResponse>(`/sites/${siteId}/notifications`, {
timeout: 30_000,
})
return {
notifications: Array.isArray(data?.notifications) ? data.notifications : [],
}
}
export type SetSiteModePayload = {
mode: string
notes: string | null
valid_until: string | null
}
export type SetSiteModeResponse = {
success: boolean
mode: string
activated_at: string
}
export async function postSiteMode(
siteId: number,
payload: SetSiteModePayload,
): Promise<SetSiteModeResponse> {
const { data } = await client.post<SetSiteModeResponse>(`/sites/${siteId}/mode`, payload)
return data
}
export async function getCurrentPlan(siteId: number): Promise<CurrentPlanResponse> {
const { data } = await client.get<CurrentPlanResponse>(`/sites/${siteId}/plan/current`, {
timeout: 60_000,
})
return data
}
/** GET /api/v1/sites/{id}/prices?date=YYYY-MM-DD */
export type SiteEffectivePriceRowDto = {
site_id: number
interval_start: string
interval_end?: string
effective_buy_price_czk_kwh?: number | string | null
effective_sell_price_czk_kwh?: number | string | null
}
export async function getSitePrices(siteId: number, date: string): Promise<SiteEffectivePriceRowDto[]> {
const { data } = await client.get<SiteEffectivePriceRowDto[]>(`/sites/${siteId}/prices`, {
params: { date },
timeout: 60_000,
})
return Array.isArray(data) ? data : []
}
export type ForecastPvIntervalRow = {
interval_start: string
power_w?: number | string | null
pv_array_id?: number
}
export type ForecastPvDayResponse = {
pv_a: ForecastPvIntervalRow[]
pv_b: ForecastPvIntervalRow[]
}
export async function getSiteForecastPv(siteId: number, date: string): Promise<ForecastPvDayResponse> {
const { data } = await client.get<ForecastPvDayResponse>(`/sites/${siteId}/forecast/pv`, {
params: { date },
timeout: 60_000,
})
return {
pv_a: Array.isArray(data?.pv_a) ? data.pv_a : [],
pv_b: Array.isArray(data?.pv_b) ? data.pv_b : [],
}
}
export type NegPricePredictionDto = {
predicted_date: string
window_start_hour: number
window_end_hour: number
probability_pct: number
expected_min_price: number | null
reason: string
}
export type NegativePredictionsResponseDto = {
predictions: NegPricePredictionDto[]
insufficient_history: boolean
}
export async function getNegativePricePredictions(
siteId: number,
): Promise<NegativePredictionsResponseDto> {
const { data } = await client.get<NegativePredictionsResponseDto>(
`/sites/${siteId}/prices/negative-predictions`,
{ timeout: 30_000 },
)
return {
predictions: Array.isArray(data?.predictions) ? data.predictions : [],
insufficient_history: Boolean(data?.insufficient_history),
}
}
export async function postRunPlan(
siteId: number,
planType: 'daily' | 'rolling',
): Promise<RunPlanResponse> {
const { data } = await client.post<RunPlanResponse>(
`/sites/${siteId}/plan/run`,
null,
{ params: { type: planType }, timeout: 120_000 },
)
return data
}
export type PricesImportResponse = {
slots_imported: number
date: string
first_price_czk_kwh: number
}
export async function postImportSitePrices(
siteId: number,
date?: string,
): Promise<PricesImportResponse> {
const { data } = await client.post<PricesImportResponse>(
`/sites/${siteId}/prices/import`,
null,
{
params: date ? { date } : undefined,
timeout: 60_000,
},
)
return data
}
export type ForecastRunResponse = {
intervals_saved: number
pv_arrays: number
}
export async function postRunForecast(siteId: number): Promise<ForecastRunResponse> {
const { data } = await client.post<ForecastRunResponse>(
`/sites/${siteId}/forecast/run`,
null,
{ timeout: 120_000 },
)
return data
}
/** Aktivní EV session (GET .../ev/sessions/active) join vozidlo + nabíječka */
export type ActiveEvSessionRow = {
id: number
charger_id: number
vehicle_id: number | null
session_start: string
energy_delivered_wh: number
target_soc_pct: number | null
target_deadline: string | null
make: string | null
model: string | null
battery_capacity_kwh: number | null
default_target_soc_pct: number | null
default_deadline_hour: number | null
charger_code: string
charger_name: string | null
}
export async function getActiveEvSessions(siteId: number): Promise<ActiveEvSessionRow[]> {
const { data } = await client.get<ActiveEvSessionRow[]>(
`/sites/${siteId}/ev/sessions/active`,
)
return data
}
export type PatchEvSessionPayload = {
target_soc_pct: number | null
target_deadline: string | null
}
export type PatchEvSessionResponse = {
success: boolean
session_id: number
}
export async function patchEvSession(
siteId: number,
sessionId: number,
payload: PatchEvSessionPayload,
): Promise<PatchEvSessionResponse> {
const { data } = await client.patch<PatchEvSessionResponse>(
`/sites/${siteId}/ev/sessions/${sessionId}`,
payload,
)
return data
}
/** Živé hodnoty registrů Deye (GET …/control/registers). */
export type DeyeRegistersLive = {
reg108_charge_a: number
reg109_discharge_a: number
reg141_energy_mode: number
reg142_limit_control: number
reg143_export_limit_w: number
reg178_peak_shaving_switch: number
reg191_peak_shaving_w: number
read_at: string
}
export async function getDeyeRegisters(siteId: number): Promise<DeyeRegistersLive> {
const { data } = await client.get<DeyeRegistersLive>(`/sites/${siteId}/control/registers`, {
timeout: 15_000,
})
return data
}
export type ModbusJournalCommandDto = {
id: number
register: number
register_name: string | null
value_to_write: number
value_written: number | null
value_verified: number | null
status: string
attempt_count: number
created_at: string
}
export type ModbusJournalResponse = {
commands: ModbusJournalCommandDto[]
}
export async function getCommandJournal(
siteId: number,
limit = 50,
): Promise<ModbusJournalResponse> {
const { data } = await client.get<ModbusJournalResponse>(
`/sites/${siteId}/control/journal`,
{ params: { limit }, timeout: 15_000 },
)
return {
commands: Array.isArray(data?.commands) ? data.commands : [],
}
}
export { client as backendClient }