update kiosk
This commit is contained in:
+277
-5
@@ -972,6 +972,173 @@ if (($_GET['a'] ?? '') === 'data') {
|
||||
margin-right: .8rem;
|
||||
}
|
||||
|
||||
/* meteo */
|
||||
.wxs {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 34rem minmax(0, 1fr);
|
||||
gap: 1.4rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.wnow {
|
||||
padding: 2rem 2.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: radial-gradient(600px 300px at 30% 20%, rgba(252, 211, 77, .14), transparent 70%), var(--panel);
|
||||
}
|
||||
|
||||
.wbig {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.4rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.wbig .wi {
|
||||
font-size: 8rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wbig span {
|
||||
font-size: 8.5rem;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wdesc {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 800;
|
||||
margin-top: .8rem;
|
||||
}
|
||||
|
||||
.wmeta {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem 1.4rem;
|
||||
margin-top: auto;
|
||||
padding-top: 1.4rem;
|
||||
}
|
||||
|
||||
.wmeta span {
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
color: var(--label);
|
||||
}
|
||||
|
||||
.wmeta b {
|
||||
font-size: 1.9rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.wright {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.4rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.whours {
|
||||
padding: 1.4rem 1.8rem;
|
||||
}
|
||||
|
||||
.hrow {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||
gap: .8rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.hc {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
}
|
||||
|
||||
.hc .t {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 800;
|
||||
color: var(--label);
|
||||
}
|
||||
|
||||
.hc .wi {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.hc b {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.hc .r,
|
||||
.dc .dr {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 800;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.hi {
|
||||
color: #60a5fa !important;
|
||||
}
|
||||
|
||||
.mid {
|
||||
color: #93c5fd !important;
|
||||
}
|
||||
|
||||
.wdays {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 1.2rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.dc {
|
||||
min-width: 0;
|
||||
padding: 1.4rem 1rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: .4rem;
|
||||
}
|
||||
|
||||
.dc .dn {
|
||||
white-space: nowrap;
|
||||
font-size: 1.7rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.dc .wi {
|
||||
font-size: 4rem;
|
||||
margin: .4rem 0;
|
||||
}
|
||||
|
||||
.dc .dt b {
|
||||
font-size: 2.6rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.dc .dt span {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
color: var(--muted);
|
||||
margin-left: .6rem;
|
||||
}
|
||||
|
||||
.dc .dd {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--label);
|
||||
min-height: 2.8rem;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin: auto;
|
||||
color: var(--muted);
|
||||
@@ -1101,6 +1268,12 @@ if (($_GET['a'] ?? '') === 'data') {
|
||||
i: 'bi-list-ol',
|
||||
f: slideQueue
|
||||
});
|
||||
if (has('meteo') && WX && WX.daily && WX.hourly) S.push({
|
||||
k: 'meteo',
|
||||
t: 'Meteo' + (D.settings.city ? ' · ' + D.settings.city : ''),
|
||||
i: 'bi-cloud-sun-fill',
|
||||
f: slideWeather
|
||||
});
|
||||
if (has('sicurezza') && D.safety) S.push({
|
||||
k: 'sicurezza',
|
||||
t: 'Sicurezza',
|
||||
@@ -1152,7 +1325,8 @@ if (($_GET['a'] ?? '') === 'data') {
|
||||
return setTimeout(next, 10000);
|
||||
}
|
||||
show(seq[pos]);
|
||||
const sec = D.settings.slide_seconds * (seq[pos].k === 'comunicazioni' || seq[pos].k === 'turni' || seq[pos].k === 'menu' ? 1.3 : 1);
|
||||
// durata impostata per questo tipo di slide, altrimenti quella predefinita (un po' di più per i testi)
|
||||
const sec = (D.settings.durations || {})[seq[pos].k] || D.settings.slide_seconds * (seq[pos].k === 'comunicazioni' || seq[pos].k === 'turni' || seq[pos].k === 'menu' ? 1.3 : 1);
|
||||
const p = $('#prog');
|
||||
p.style.transition = 'none';
|
||||
p.style.width = '0';
|
||||
@@ -1461,18 +1635,116 @@ if (($_GET['a'] ?? '') === 'data') {
|
||||
setInterval(weather, 30 * 60000);
|
||||
}
|
||||
|
||||
// meteo: ora, prossime ore e prossimi giorni (open-meteo, gratuito e senza chiave)
|
||||
let WX = null;
|
||||
const WX_TXT = {
|
||||
0: 'Sereno',
|
||||
1: 'Quasi sereno',
|
||||
2: 'Parzialmente nuvoloso',
|
||||
3: 'Coperto',
|
||||
45: 'Nebbia',
|
||||
48: 'Nebbia con brina',
|
||||
51: 'Pioviggine leggera',
|
||||
53: 'Pioviggine',
|
||||
55: 'Pioviggine intensa',
|
||||
56: 'Pioviggine gelata',
|
||||
57: 'Pioviggine gelata',
|
||||
61: 'Pioggia debole',
|
||||
63: 'Pioggia',
|
||||
65: 'Pioggia forte',
|
||||
66: 'Pioggia gelata',
|
||||
67: 'Pioggia gelata forte',
|
||||
71: 'Neve debole',
|
||||
73: 'Neve',
|
||||
75: 'Neve forte',
|
||||
77: 'Nevischio',
|
||||
80: 'Rovesci leggeri',
|
||||
81: 'Rovesci',
|
||||
82: 'Rovesci violenti',
|
||||
85: 'Rovesci di neve',
|
||||
86: 'Rovesci di neve forti',
|
||||
95: 'Temporale',
|
||||
96: 'Temporale con grandine',
|
||||
99: 'Temporale con grandine'
|
||||
};
|
||||
|
||||
function wxIcon(w, day = true) {
|
||||
w = +w;
|
||||
if (w === 0) return day ? ['bi-sun-fill', '#fcd34d'] : ['bi-moon-stars-fill', '#c7d2fe'];
|
||||
if (w <= 2) return day ? ['bi-cloud-sun-fill', '#fcd34d'] : ['bi-cloud-moon-fill', '#c7d2fe'];
|
||||
if (w === 3) return ['bi-cloud-fill', '#cbd5e1'];
|
||||
if (w <= 48) return ['bi-cloud-fog2-fill', '#94a3b8'];
|
||||
if (w <= 57) return ['bi-cloud-drizzle-fill', '#7dd3fc'];
|
||||
if (w <= 67) return ['bi-cloud-rain-fill', '#60a5fa'];
|
||||
if (w <= 77) return ['bi-snow', '#e0f2fe'];
|
||||
if (w <= 82) return ['bi-cloud-rain-heavy-fill', '#3b82f6'];
|
||||
if (w <= 86) return ['bi-cloud-snow-fill', '#e0f2fe'];
|
||||
return ['bi-cloud-lightning-rain-fill', '#a78bfa'];
|
||||
}
|
||||
const ico = (w, day, cls = '') => {
|
||||
const [i, c] = wxIcon(w, day);
|
||||
return `<i class="bi ${i} wi ${cls}" style="color:${c}"></i>`;
|
||||
};
|
||||
|
||||
function weather() {
|
||||
const s = D.settings;
|
||||
fetch(`https://api.open-meteo.com/v1/forecast?latitude=${encodeURIComponent(s.lat)}&longitude=${encodeURIComponent(s.lon)}¤t=temperature_2m,weather_code&timezone=auto`)
|
||||
const q = `latitude=${encodeURIComponent(s.lat)}&longitude=${encodeURIComponent(s.lon)}&timezone=Europe%2FRome&forecast_days=7` +
|
||||
'¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code,is_day' +
|
||||
'&hourly=temperature_2m,weather_code,precipitation_probability,is_day' +
|
||||
'&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max,sunrise,sunset';
|
||||
fetch('https://api.open-meteo.com/v1/forecast?' + q)
|
||||
.then(r => r.json()).then(r => {
|
||||
const c = r.current;
|
||||
if (!c) return;
|
||||
const w = +c.weather_code;
|
||||
const ic = w === 0 ? 'bi-sun-fill' : w <= 2 ? 'bi-cloud-sun-fill' : w === 3 ? 'bi-cloud-fill' : w <= 48 ? 'bi-cloud-fog2-fill' : w <= 67 ? 'bi-cloud-rain-fill' : w <= 77 ? 'bi-snow' : w <= 82 ? 'bi-cloud-rain-heavy-fill' : 'bi-cloud-lightning-rain-fill';
|
||||
$('#wx').innerHTML = `<i class="bi ${ic}"></i><div>${Math.round(c.temperature_2m)}°<small>${esc(s.city || '')}</small></div>`;
|
||||
WX = r;
|
||||
$('#wx').innerHTML = `${ico(c.weather_code, c.is_day !== 0)}<div>${Math.round(c.temperature_2m)}°<small>${esc(s.city || '')}</small></div>`;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function slideWeather() {
|
||||
const c = WX.current,
|
||||
d = WX.daily,
|
||||
h = WX.hourly;
|
||||
const hhmm = t => String(t).slice(11, 16);
|
||||
// prossime 8 ore a partire dall'ora attuale
|
||||
const nowH = String(c.time).slice(0, 13);
|
||||
let i0 = h.time.findIndex(t => t.slice(0, 13) >= nowH);
|
||||
if (i0 < 0) i0 = 0;
|
||||
const hours = [];
|
||||
for (let i = i0 + 1; i < h.time.length && hours.length < 8; i++) hours.push(i);
|
||||
const GG = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'];
|
||||
const days = d.time.map((t, i) => i).slice(1, 7);
|
||||
const rainCls = p => p >= 60 ? 'hi' : (p >= 30 ? 'mid' : '');
|
||||
return `<div class="wxs">
|
||||
<div class="panel wnow">
|
||||
<div class="lbl">Adesso${D.settings.city ? ' a ' + esc(D.settings.city) : ''}</div>
|
||||
<div class="wbig">${ico(c.weather_code, c.is_day !== 0)}<span>${Math.round(c.temperature_2m)}°</span></div>
|
||||
<div class="wdesc">${esc(WX_TXT[c.weather_code] || '')}</div>
|
||||
<div class="wmeta">
|
||||
<div><span>Percepita</span><b>${Math.round(c.apparent_temperature)}°</b></div>
|
||||
<div><span>Min / max</span><b>${Math.round(d.temperature_2m_min[0])}° / ${Math.round(d.temperature_2m_max[0])}°</b></div>
|
||||
<div><span>Umidità</span><b>${Math.round(c.relative_humidity_2m)}%</b></div>
|
||||
<div><span>Vento</span><b>${Math.round(c.wind_speed_10m)} km/h</b></div>
|
||||
<div><span>Pioggia oggi</span><b>${d.precipitation_probability_max[0] ?? 0}%</b></div>
|
||||
<div><span>Alba · tramonto</span><b>${hhmm(d.sunrise[0])} · ${hhmm(d.sunset[0])}</b></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wright">
|
||||
<div class="panel whours">
|
||||
<div class="lbl">Prossime ore</div>
|
||||
<div class="hrow">${hours.map(i => `<div class="hc"><span class="t">${hhmm(h.time[i])}</span>${ico(h.weather_code[i], h.is_day[i] !== 0)}<b>${Math.round(h.temperature_2m[i])}°</b><span class="r ${rainCls(h.precipitation_probability[i])}"><i class="bi bi-droplet-fill"></i> ${h.precipitation_probability[i] ?? 0}%</span></div>`).join('')}</div>
|
||||
</div>
|
||||
<div class="wdays">${days.map(i => {
|
||||
const dt = new Date(d.time[i] + 'T12:00:00');
|
||||
return `<div class="panel dc"><div class="dn">${i === 1 ? 'Domani' : GG[dt.getDay()] + ' ' + dt.getDate()}</div>${ico(d.weather_code[i], true)}
|
||||
<div class="dt"><b>${Math.round(d.temperature_2m_max[i])}°</b><span>${Math.round(d.temperature_2m_min[i])}°</span></div>
|
||||
<div class="dd">${esc(WX_TXT[d.weather_code[i]] || '')}</div>
|
||||
<div class="dr ${rainCls(d.precipitation_probability_max[i])}"><i class="bi bi-droplet-fill"></i> ${d.precipitation_probability_max[i] ?? 0}%</div></div>`;
|
||||
}).join('')}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/* ---------- avvio ---------- */
|
||||
tick();
|
||||
load().then(() => next());
|
||||
|
||||
Reference in New Issue
Block a user