Add mobile home page with weather and transit widgets
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -17,6 +17,7 @@ import jellyfin from './routes/jellyfin'
|
||||
import navidrome from './routes/navidrome'
|
||||
import romm from './routes/romm'
|
||||
import weather from './routes/weather'
|
||||
import transit from './routes/transit'
|
||||
|
||||
const app = express()
|
||||
const PORT = Number(process.env.PORT ?? 3001)
|
||||
@@ -39,6 +40,7 @@ app.use('/api/jellyfin', jellyfin)
|
||||
app.use('/api/navidrome', navidrome)
|
||||
app.use('/api/romm', romm)
|
||||
app.use('/api/weather', weather)
|
||||
app.use('/api/transit', transit)
|
||||
|
||||
// Serve built frontend in production only
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user