Add mobile home page with weather and transit widgets
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-05-16 16:33:51 +02:00
parent 7bbefaa1f6
commit 13339b17bf
8 changed files with 486 additions and 15 deletions
+36
View File
@@ -0,0 +1,36 @@
import { Router } from 'express'
import axios from 'axios'
const router = Router()
router.get('/departures', async (_req, res) => {
try {
const stop = process.env.TRANSIT_STOP_VAG ?? 'SCHW'
const response = await axios.get(
`https://start.vag.de/dm/api/abfahrten.json/vag/${stop}?timespan=90&limitcount=25`,
{ timeout: 8000 }
)
const d = response.data
const departures = (d.Abfahrten ?? []).map((a: Record<string, unknown>) => ({
line: a.Linienname,
direction: a.Richtungstext,
scheduledTime: a.AbfahrtszeitSoll,
realtimeTime: a.AbfahrtszeitIst,
realtime: a.Prognose,
product: a.Produkt,
platform: a.HaltesteigText,
}))
res.json({
stop: d.Haltestellenname ?? stop,
notices: d.Sonderinformationen ?? [],
departures,
})
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Unknown error'
res.status(500).json({ error: msg })
}
})
export default router
+22 -10
View File
@@ -13,20 +13,32 @@ router.get('/current', async (_req, res) => {
const response = await axios.get(
`https://wttr.in/${encodeURIComponent(location)}?format=j1`,
{ headers: { 'Accept': 'application/json' }, timeout: 8000 }
{ headers: { Accept: 'application/json' }, timeout: 8000 }
)
const c = response.data.current_condition[0]
const area = response.data.nearest_area?.[0]
const data = response.data
const c = data.current_condition[0]
const area = data.nearest_area?.[0]
const today = data.weather?.[0]
const hourly = (today?.hourly ?? []).map((h: Record<string, unknown>) => ({
hour: Math.floor(Number(h.time) / 100),
tempC: Number(h.tempC),
code: Number(h.weatherCode),
desc: (h.weatherDesc as { value: string }[])[0]?.value ?? '',
}))
res.json({
tempC: Number(c.temp_C),
feelsLikeC: Number(c.FeelsLikeC),
humidity: Number(c.humidity),
windKmph: Number(c.windspeedKmph),
desc: c.weatherDesc[0].value as string,
code: Number(c.weatherCode),
city: area?.areaName?.[0]?.value ?? location,
tempC: Number(c.temp_C),
feelsLikeC: Number(c.FeelsLikeC),
humidity: Number(c.humidity),
windKmph: Number(c.windspeedKmph),
desc: c.weatherDesc[0].value as string,
code: Number(c.weatherCode),
city: area?.areaName?.[0]?.value ?? location,
todayMin: Number(today?.mintempC ?? c.temp_C),
todayMax: Number(today?.maxtempC ?? c.temp_C),
hourly,
})
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Unknown error'