cff-displays/index.js
2024-04-09 09:53:14 +02:00

221 lines
5.9 KiB
JavaScript

const MAX_DISPLAYED_VIAS = 4
function fetchInfo(bpuic) {
return fetch(`https://displays.api.sbb.ch/internal/api/v1/data/${bpuic}`, {
headers: {
"X-API-Key": API_KEY
}
}).then(res => {
return res.json()
})
}
const example = {
config: {
stationName: ""
},
contents: [
{
type: "GA",
verkehrsmittels: [
{
vmArt: "IR",
liniennummer: 90,
dsVmArt: {
vmArt: "IR",
ttsTexte: {
FR: "InterRegio"
}
},
zeitAbKb: "2024-04-08T18:32:00", // heure de départ prévue
zeitAbErw: "2024-04-08T18:33:00", // heure de départ réelle
gleisAbKb: 1, // voie prévue
gleisAbIst: 1, // voie réelle
verspaetungsminutenAb: 0, // au moins n minutes de retard
verspaetungsminutenAn: 0, // au plus n minutes de retard
ziele: [
{
betriebspunkt: {
bpuic: 8501609,
bezeichnungOffiziell: "Brig"
}
}
],
vias: [
{
betriebspunkt: {
bpuic: 8501500,
bezeichnungOffiziell: "Martigny"
},
prio: 601,
trennung: false // séparation
}
],
formation: {
richtung: "RECHTS",
waggons: [
{
sektor: "A",
form: "MITTE", // icône (MITTE, START_LINKS, END_RECHT)
typ: "FIKTIV", // classe (FIKTIV = rien, ERSTE, ZWEITE, ERSTE_ZWEITE, RESTAURANT_ERSTE, FAMILIENWAGEN, TRIEBFAHRZEUG = loco)
angebote: [], // services (vélos, resto, etc.)
statusList: [], // status (GESCHLOSSEN)
nummer: null,
zielIndex: null // terminus (utile si séparation)
},
{
sektor: "A",
form: "MITTE",
typ: "ZWEITE",
angebote: [
"VELOHAKEN" // NIEDERFLUREINSTIEG, ROLLSTUHLSTELLPLAETZE, KINDERWAGEN, RESERVATIONSPFLICHTIGE
],
nummer: null,
zielIndex: 0
},
{
sektor: "A",
form: "MITTE",
typ: "ZWEITE",
angebote: [
"VELOHAKEN"
],
nummer: null,
zielIndex: 0
}
],
ziele: [
{
zielIndex: 0,
betriebspunkt: {
bpuic: 8501609,
bezeichnungOffiziell: "Brig"
},
fromPos: 1, // premier wagon concerné
toPos: 10 // dernier wagon concerné
}
]
}
}
]
}
]
}
function getTimeFromTS(timestamp) {
const date = new Date(timestamp)
const hours = date.getHours().toString().padStart(2, "0")
const minutes = date.getMinutes().toString().padStart(2, "0")
return `${hours}:${minutes}`
}
function selectVias(vias) {
if (vias.length <= MAX_DISPLAYED_VIAS) return vias
let priorities = vias.map((via, index) => [via.prio, index])
priorities = priorities.sort((a, b) => a[0] - b[0])
let selectedVias = []
let selectedPrio = priorities.slice(0, MAX_DISPLAYED_VIAS).sort((a, b) => a[1] - b[1])
selectedPrio.forEach(([prio, index]) => {
selectedVias.push(vias[index])
})
return selectedVias
}
function buildComposition(formation) {
const template = document.getElementById("waggon-template").cloneNode(true)
template.removeAttribute("id")
template.classList.remove("template")
const composition = document.getElementById("composition")
const sectors = document.getElementById("sectors")
sectors.innerHTML = ""
const carriages = document.getElementById("carriages")
carriages.innerHTML = ""
carriages.dataset.direction = formation.richtung === "RECHTS" ? "right" : "left"
const weights = {}
let first = true
for (let i = 0; i < formation.waggons.length; i++) {
const waggon = formation.waggons[i]
if (!(waggon.sektor in weights)) {
weights[waggon.sektor] = 0
}
weights[waggon.sektor]++
if (waggon.typ === "FIKTIV") {
continue
}
const elmt = template.cloneNode(true)
if (first) {
first = false
carriages.style.setProperty("--offset", i.toString())
}
elmt.classList.add("form-" + waggon.form.toLowerCase())
const cls = {
ERSTE: ["1", "first"],
ZWEITE: ["2", "second"],
ERSTE_ZWEITE: ["1|2", "first-second"],
RESTAURANT_ERSTE: ["1", "first"],
RESTAURANT_ZWEITE: ["1", "first"],
FAMILIENWAGEN: ["", "familie"],
TRIEBFAHRZEUG: ["", "loc"]
}[waggon.typ]
elmt.querySelector(".class").innerText = cls[0]
elmt.classList.add("class-" + cls[1])
if (waggon.nummer !== null) {
elmt.querySelector(".number").innerText = waggon.nummer
}
carriages.appendChild(elmt)
}
composition.style.setProperty("--n-waggons", formation.waggons.length.toString())
const sectorLetters = Object.keys(weights).sort()
sectorLetters.forEach(letter => {
const sector = document.createElement("div")
sector.classList.add("sector")
sector.style.setProperty("--weight", weights[letter].toString())
sector.innerText = letter
sectors.appendChild(sector)
})
console.log(weights)
}
function updateDisplay(result, idx=0, platform=null) {
document.getElementById("station").innerText = result.config.stationName
let trains = result.contents[0].verkehrsmittels
if (platform !== null) {
trains = trains.filter(t => t.gleisAbIst === platform)
}
const train = trains[idx]
let icon = train.vmArt
let iconTxt = train.dsVmArt.ttsTexte.FR
if (train.liniennummer !== null) {
icon += "-" + train.liniennummer
iconTxt += " " + train.liniennummer
}
icon = "icons/" + icon.toLowerCase() + "-negative.svg"
document.getElementById("train-type").src = icon
document.getElementById("train-type").alt = iconTxt
document.getElementById("departure").innerText = getTimeFromTS(train.zeitAbKb)
document.getElementById("destination").innerText = train.ziele[0].betriebspunkt.bezeichnungOffiziell
const stopsList = document.getElementById("stops")
stopsList.innerHTML = ""
const vias = selectVias(train.vias)
vias.forEach(via => {
const stop = document.createElement("div")
stop.classList.add("stop")
stop.innerText = via.betriebspunkt.bezeichnungOffiziell
stopsList.appendChild(stop)
})
buildComposition(train.formation)
}