Feat/prerender and state (#8)

* pre-load data behind link elements on hover

* Enable SSR and prerendering of pages in 'auto' mode

* merge relay & regulator state in /api/state

This helps when updating relays and receiving state of relays and regulator state (idle,
heating or cooling) after emitting relay change.
This commit is contained in:
2025-01-12 18:34:16 +01:00
committed by GitHub
parent 34018285a0
commit 4d77088764
8 changed files with 86 additions and 36 deletions

View File

@@ -20,7 +20,7 @@ const routes: Array<IRoute> = [{
<ul class="navigation-cards" on:click>
{#each routes as route}
<a href={route.path}>
<a href={route.path} data-sveltekit-preload-data="hover">
<li>
<span>{ route.name }</span>

View File

@@ -7,13 +7,13 @@
export let relays: IRelaysDTO[] = [];
const dispatch = createEventDispatcher();
function toggleRelay(controls: string) {
async function toggleRelay(controls: string) {
const url = `/api/relay/${controls}`;
const options = {
method: 'POST'
};
fetch(url, options)
await fetch(url, options)
.then((resp) => resp.json())
.then((response) => {
const changedRelay = relays.findIndex((relay) => relay.pin === response.pin);

16
src/routes/+layout.ts Normal file
View File

@@ -0,0 +1,16 @@
// if you want to generate a static html file
// for your page.
// Documentation: https://kit.svelte.dev/docs/page-options#prerender
export const prerender = 'auto';
// if you want to Generate a SPA
// you have to set ssr to false.
// This is not the case (so set as true or comment the line)
// Documentation: https://kit.svelte.dev/docs/page-options#ssr
export const ssr = true;
// How to manage the trailing slashes in the URLs
// the URL for about page witll be /about with 'ignore' (default)
// the URL for about page witll be /about/ with 'always'
// https://kit.svelte.dev/docs/page-options#trailingslash
export const trailingSlash = 'ignore';

View File

@@ -1,28 +1,51 @@
import { BREWLOGGER_HOST } from '$env/static/private';
import type { PageServerLoad } from './$types';
import type { ISensorDTO } from '../lib/interfaces/ISensorDTO'
import type { PageServerLoad, RequestEvent } from './$types';
import type { ISensorDTO } from '../lib/interfaces/ISensorDTO';
import { IRelaysDTO } from '../lib/interfaces/IRelaysDTO';
import { IStateDTO } from '../lib/interfaces/IStateDTO';
const sensorsUrl = `${BREWLOGGER_HOST}/api/sensors`;
const relaysUrl = `${BREWLOGGER_HOST}/api/relays`;
const stateUrl = `${BREWLOGGER_HOST}/api/regulator`;
export const prerender = true; // explicitly pre-render
async function getFromEndpoint(endpoint: string) {
return fetch(endpoint)
async function fetchSensors(fetch: Fetch): Promise<ISensorDTO[]> {
return fetch(sensorsUrl)
.then((resp) => resp.json())
.then((data) => data?.sensors || [])
.catch((error) => {
console.error('Failed to fetch endpoint:', endpoint);
console.error('failed to fetch sensors.');
console.error(error);
return null
return null;
});
}
export const load: PageServerLoad = async () => {
const [sensorsResp, relaysResp, stateResp] = await Promise.all([getFromEndpoint(sensorsUrl), getFromEndpoint(relaysUrl), getFromEndpoint(stateUrl)]);
const sensors: ISensorDTO[] = sensorsResp?.sensors || []
const relays: IRelaysDTO[] = relaysResp?.relays || []
const state: IStateDTO = stateResp
// calls internal sveltekit api endpoint.
// this allows unified response in svelte app, even
// though it requires multiple calls to regulator server
async function fetchState(fetch: Fetch) {
return fetch('/api/state')
.then((resp) => resp.json())
.catch((error) => {
console.error('failed to fetch state');
console.error(error);
return null;
});
}
type Fetch = typeof fetch;
type HandleFetch = {
event: RequestEvent;
request: Request;
fetch: Fetch;
};
export const load: PageServerLoad = async (input: HandleFetch) => {
const [stateResponse, sensorsResponse] = await Promise.all([
fetchState(input.fetch),
fetchSensors(input.fetch)
]);
const sensors: ISensorDTO[] = sensorsResponse || [];
const relays: IRelaysDTO[] = stateResponse?.relays || [];
const regulator: IStateDTO = stateResponse?.regulator;
const inside = sensors.find((sensor: ISensorDTO) => sensor.location === 'inside');
const outside = sensors.find((sensor: ISensorDTO) => sensor.location === 'outside');
@@ -30,7 +53,6 @@ export const load: PageServerLoad = async () => {
return {
inside: inside || null,
outside: outside || null,
relays,
state
state: { regulator, relays }
};
};

View File

@@ -8,14 +8,13 @@
export let data: PageData;
const { inside, outside } = data;
let { relays, state } = data;
let { relays, regulator } = data.state;
const updateState = () =>
setTimeout(() => {
fetch('/api/state')
.then((resp) => resp.json())
.then((response: IStateDTO) => (state = response));
}, 100);
const updateState = () => {
fetch('/api/state')
.then((resp) => resp.json())
.then((response: IStateDTO) => ({ relays, regulator } = response));
}
</script>
<Logo />
@@ -23,7 +22,12 @@
<div class="vertical-grid">
<BrewProgress />
<VerticalSensorDisplay {inside} {outside} {relays} {state} />
<VerticalSensorDisplay
inside="{inside}"
outside="{outside}"
relays="{relays}"
state="{regulator}"
/>
<RelayControls bind:relays="{relays}" on:relaySwitched="{updateState}" />
</div>

View File

@@ -1,9 +1,19 @@
import { json, RequestEvent } from '@sveltejs/kit';
import { BREWLOGGER_HOST } from '$env/static/private';
import type { RequestHandler } from './$types';
import { IRelaysDTO } from '../../../lib/interfaces/IRelaysDTO';
export const GET = (async (event: RequestEvent) => {
return fetch(BREWLOGGER_HOST + '/api/regulator')
async function fetchRegulator(): Promise<Response | IRelaysDTO[]> {
return fetch(BREWLOGGER_HOST + '/api/regulator').then((resp) => resp.json());
}
async function fetchRelays(): Promise<Response | IRelaysDTO[]> {
return fetch(BREWLOGGER_HOST + '/api/relays')
.then((resp) => resp.json())
.then((response) => json(response));
.then((data) => data?.relays || []);
}
export const GET = (async () => {
const [regulatorState, relaysState] = await Promise.all([fetchRegulator(), fetchRelays()]);
return json({ regulator: regulatorState, relays: relaysState });
}) satisfies RequestHandler;

View File

@@ -17,7 +17,7 @@
<ul>
{#each brews as brew}
<li class="brew">
<a href="{path(brew.date)}">
<a href="{path(brew.date)}" data-sveltekit-preload-data="hover">
<img src="/images/{brew.image}" alt="Beer label of {brew.beer.name}" />
<div class="details">

View File

@@ -4,8 +4,9 @@ import { fetchHumidity, fetchTemperature } from '../../../lib/server/graphQueryG
import type { PageLoad } from './$types';
async function fetchGraphData(brew) {
const start = new Date(brew.date * 1000 - 86400000);
const end = new Date(brew.date * 1000 + 4838400000);
const { date } = brew;
const start = new Date(date * 1000 - 86400000);
const end = new Date(date * 1000 + 4838400000);
const size = 200;
const [temperature, humidity] = await Promise.all([
@@ -13,10 +14,7 @@ async function fetchGraphData(brew) {
fetchHumidity(start, end, size)
]);
return {
temperature,
humidity
};
return { temperature, humidity };
}
export const load = (async ({ params }) => {