signalk-mareas-ihm 2.1.4 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/index.js +29 -3
- package/dist/mobile.html +118 -36
- package/package.json +1 -1
- package/public/mobile.html +118 -36
package/dist/build-info.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ function isPositionValue(v) {
|
|
|
49
49
|
// timestamp + git hash so we can verify exactly which build is running on the Pi
|
|
50
50
|
// without ambiguity. ("¿Qué versión tengo deployada?" → /api/paths or landing.)
|
|
51
51
|
const PLUGIN_VERSION = esmRequire("../package.json").version;
|
|
52
|
-
const PLUGIN_REVISION = "
|
|
52
|
+
const PLUGIN_REVISION = "Rev317";
|
|
53
53
|
let _buildInfo = null;
|
|
54
54
|
try {
|
|
55
55
|
_buildInfo = esmRequire("./build-info.json");
|
|
@@ -3507,6 +3507,24 @@ export default function (app) {
|
|
|
3507
3507
|
}
|
|
3508
3508
|
});
|
|
3509
3509
|
expressApp.get("/signalk-mareas-ihm/navtiles/:z/:x/:y.png", async (req, res) => {
|
|
3510
|
+
/* Rev317: guard res.headersSent en todos los error paths. Si la conexión
|
|
3511
|
+
upstream falla MID-STREAM (después de pipe()), no se puede llamar a
|
|
3512
|
+
res.status/send porque ya se enviaron headers → "Cannot set headers
|
|
3513
|
+
after they are sent". Solución: solo enviar 502 si headersSent=false;
|
|
3514
|
+
en otro caso destruir el socket para cortar el stream colgado. */
|
|
3515
|
+
const sendFallback = () => {
|
|
3516
|
+
if (res.headersSent) {
|
|
3517
|
+
try {
|
|
3518
|
+
res.end();
|
|
3519
|
+
}
|
|
3520
|
+
catch { /* socket already closed */ }
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
try {
|
|
3524
|
+
res.status(502).set("Content-Type", "image/png").send(TRANSPARENT_PNG);
|
|
3525
|
+
}
|
|
3526
|
+
catch { /* ignore */ }
|
|
3527
|
+
};
|
|
3510
3528
|
try {
|
|
3511
3529
|
const { token, bearer } = await fetchChartTokens();
|
|
3512
3530
|
const z = req.params.z, x = req.params.x, y = req.params.y;
|
|
@@ -3519,16 +3537,24 @@ export default function (app) {
|
|
|
3519
3537
|
"Origin": "https://maps.garmin.com"
|
|
3520
3538
|
}
|
|
3521
3539
|
}, (tileResp) => {
|
|
3540
|
+
if (res.headersSent) {
|
|
3541
|
+
try {
|
|
3542
|
+
tileResp.destroy();
|
|
3543
|
+
}
|
|
3544
|
+
catch { }
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3522
3547
|
const ct = tileResp.headers["content-type"] || "image/png";
|
|
3523
3548
|
res.set("Content-Type", ct);
|
|
3524
3549
|
res.set("Cache-Control", "public, max-age=15552000, immutable");
|
|
3550
|
+
tileResp.on("error", sendFallback);
|
|
3525
3551
|
tileResp.pipe(res);
|
|
3526
3552
|
});
|
|
3527
|
-
tileReq.on("error",
|
|
3553
|
+
tileReq.on("error", sendFallback);
|
|
3528
3554
|
tileReq.end();
|
|
3529
3555
|
}
|
|
3530
3556
|
catch {
|
|
3531
|
-
|
|
3557
|
+
sendFallback();
|
|
3532
3558
|
}
|
|
3533
3559
|
});
|
|
3534
3560
|
// ═══════════════ SSE (Server-Sent Events) for multi-device sync ═══════════════
|
package/dist/mobile.html
CHANGED
|
@@ -1303,26 +1303,24 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1303
1303
|
<!-- Rev296: botones izquierdos dentro de #m-leftbar (scroll vertical, mismo
|
|
1304
1304
|
estilo que #m-sidebar derecha). -->
|
|
1305
1305
|
<div id="m-leftbar">
|
|
1306
|
-
<button id="m-ham" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1307
|
-
<button id="m-cartas" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1306
|
+
<button id="m-ham" data-i18n-title="lb_ham_tip" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1307
|
+
<button id="m-cartas" data-i18n-title="lb_audio_tip" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1308
1308
|
<span class="ico" id="m-mute-icon">🔊</span>
|
|
1309
1309
|
<span class="lbl" data-i18n="m_silencio">Silencio</span>
|
|
1310
1310
|
</button>
|
|
1311
|
-
<button id="m-snooze" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1311
|
+
<button id="m-snooze" data-i18n-title="lb_snooze_tip" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1312
1312
|
<span class="ico" id="m-snooze-icon">💤</span>
|
|
1313
1313
|
<span class="lbl" id="m-snooze-lbl" data-i18n="m_snooze">Snooze</span>
|
|
1314
1314
|
</button>
|
|
1315
|
-
<button id="m-fav" title="Fondeo favorito" onclick="m_favClick()">
|
|
1315
|
+
<button id="m-fav" data-i18n-title="lb_fav_tip" title="Fondeo favorito" onclick="m_favClick()">
|
|
1316
1316
|
<span class="ico">❤</span>
|
|
1317
1317
|
<span class="lbl" data-i18n="m_favorito">Favorito</span>
|
|
1318
1318
|
</button>
|
|
1319
|
-
|
|
1320
|
-
(entre Favorito y KIP). Mismos handlers que los del sidebar derecho. -->
|
|
1321
|
-
<button id="m-lb-curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1319
|
+
<button id="m-lb-curvas" data-i18n-title="sb_curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1322
1320
|
<span class="ico">〰️</span>
|
|
1323
1321
|
<span class="lbl" data-i18n="sb_curvas">Curvas</span>
|
|
1324
1322
|
</button>
|
|
1325
|
-
<button id="m-lb-mareas" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1323
|
+
<button id="m-lb-mareas" data-i18n-title="lb_mareas_tip" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1326
1324
|
<span class="ico" style="display:inline-flex;align-items:center;justify-content:center">
|
|
1327
1325
|
<svg viewBox="0 0 44 44" width="30" height="30">
|
|
1328
1326
|
<rect x="2" y="2" width="40" height="14" rx="2" fill="#ffd966"/>
|
|
@@ -1334,10 +1332,10 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1334
1332
|
</span>
|
|
1335
1333
|
<span class="lbl" data-i18n="sb_mareas">Mareas</span>
|
|
1336
1334
|
</button>
|
|
1337
|
-
<button id="m-kip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1335
|
+
<button id="m-kip" data-i18n-title="lb_kip_tip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1338
1336
|
<span class="ico" style="font-weight:900;letter-spacing:1px">KIP</span>
|
|
1339
1337
|
</button>
|
|
1340
|
-
<button id="m-fb" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1338
|
+
<button id="m-fb" data-i18n-title="lb_fb_tip" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1341
1339
|
<img src="assets/freeboard.png" alt="Freeboard"
|
|
1342
1340
|
style="width:42px;height:42px;object-fit:contain;background:#fff;border-radius:6px;padding:2px;display:block;box-sizing:border-box"/>
|
|
1343
1341
|
</button>
|
|
@@ -1389,7 +1387,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1389
1387
|
<span class="vl" id="m-bb-wind-kt-txt">—<span style="font-size:11px;font-weight:700;opacity:.75;margin-left:2px">kts</span></span>
|
|
1390
1388
|
</div>
|
|
1391
1389
|
</div>
|
|
1392
|
-
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop')"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1390
|
+
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop');typeof fetchSondaData==='function'&&fetchSondaData()"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1393
1391
|
<div class="it" data-i18n-title="bb_cadena_tip" title="Cadena recomendada" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_cadena_rec">Cadena rec.</span><span class="vl" id="m-bb-cad">—</span></div>
|
|
1394
1392
|
<div class="it" data-i18n-title="bb_dist_tip" title="Distancia al ancla" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_dist_ancla">Dist. ancla</span><span class="vl" id="m-bb-dist">—</span></div>
|
|
1395
1393
|
<div class="it m-bb-graph" data-i18n-title="bb_presion_tip" title="Presión barométrica · línea naranja = AHORA · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
|
|
@@ -1593,18 +1591,20 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1593
1591
|
</div>
|
|
1594
1592
|
<!-- Popup info histórico ola: leyenda colores + tabla detallada. -->
|
|
1595
1593
|
<div class="popup-overlay" id="wave-hist-info-pop" onclick="closePopup('wave-hist-info-pop')">
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
<
|
|
1600
|
-
<
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1604
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1605
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1594
|
+
<!-- Rev316: ventana wave-hist-info ampliada (antes 620px / 88vh) y todo
|
|
1595
|
+
bilingüe. Estilo título alineado con resto de modales (.m-modal-title). -->
|
|
1596
|
+
<div class="popup-box" onclick="event.stopPropagation()" style="width:900px;max-width:96vw;max-height:92vh;overflow-y:auto;padding:28px 32px">
|
|
1597
|
+
<button onclick="closePopup('wave-hist-info-pop')" style="position:absolute;top:10px;right:14px;background:none;border:none;color:#aaa;font-size:28px;cursor:pointer;line-height:1">✕</button>
|
|
1598
|
+
<h4 style="margin:0 0 12px 0;font-size:24px;font-weight:900;color:#fff" data-i18n="sh_wh_title">Historial de olas — leyenda y detalle</h4>
|
|
1599
|
+
<div style="font-size:16px;color:#9ad;margin-bottom:18px" data-i18n="sh_wh_subtitle">Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.</div>
|
|
1600
|
+
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:22px">
|
|
1601
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#3aa856;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="m_calma">Calma</div><div style="font-size:12px;color:#888"><0.5°</div></div>
|
|
1602
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#a3c813;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_rizada">Rizada</div><div style="font-size:12px;color:#888">0.5–1.5°</div></div>
|
|
1603
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f3c812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_moderada">Moderada</div><div style="font-size:12px;color:#888">1.5–3°</div></div>
|
|
1604
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f37812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_agitada">Agitada</div><div style="font-size:12px;color:#888">3–6°</div></div>
|
|
1605
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#e74c3c;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_fuerte">Fuerte</div><div style="font-size:12px;color:#888">>6°</div></div>
|
|
1606
1606
|
</div>
|
|
1607
|
-
<div id="wave-hist-info-table" style="font-size:
|
|
1607
|
+
<div id="wave-hist-info-table" style="font-size:15px;color:#cfd8dc"></div>
|
|
1608
1608
|
</div>
|
|
1609
1609
|
</div>
|
|
1610
1610
|
<!-- Rev79: popup explicacion del % de proteccion -->
|
|
@@ -1776,6 +1776,12 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1776
1776
|
<div class="alarm-info"><b style="color:var(--red)" data-i18n="alarma_sonda">🌊 Alarma Sonda</b><br><small style="color:#aaa" data-i18n="alarma_sonda_desc">Riesgo de varada por profundidad</small><div class="alarm-st" id="alarm-sonda-st"></div></div>
|
|
1777
1777
|
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-sonda" onclick="event.stopPropagation()" onchange="setAlarm('sonda',this.checked)"><label for="chk-alarm-sonda" onclick="event.stopPropagation()"></label></div>
|
|
1778
1778
|
</div>
|
|
1779
|
+
<!-- Rev316: nuevo selector "Mala condición climática" — alerta cuando
|
|
1780
|
+
la previsión meteo en las próximas 6h tiene viento > 25 kt o ola > 1.5 m. -->
|
|
1781
|
+
<div class="alarm-row" id="ar-weather" onclick="var c=document.getElementById('chk-alarm-weather');c.checked=!c.checked;setAlarm('weather',c.checked)">
|
|
1782
|
+
<div class="alarm-info"><b style="color:#ffb23f" data-i18n="alarma_meteo">⛈️ Mala condición climática</b><br><small style="color:#aaa" data-i18n="alarma_meteo_desc">Aviso si previsión 6h supera viento 25 kt u ola 1.5 m</small><div class="alarm-st" id="alarm-weather-st"></div></div>
|
|
1783
|
+
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-weather" onclick="event.stopPropagation()" onchange="setAlarm('weather',this.checked)"><label for="chk-alarm-weather" onclick="event.stopPropagation()"></label></div>
|
|
1784
|
+
</div>
|
|
1779
1785
|
</div>
|
|
1780
1786
|
<div id="alarm-active-list" style="margin-top:6px"></div>
|
|
1781
1787
|
<!-- Rev184: orden correcto — debajo de alarmas va el VOLUMEN, luego
|
|
@@ -2078,6 +2084,31 @@ var _i18n={
|
|
|
2078
2084
|
m_viento:{es:'Viento',en:'Wind'},
|
|
2079
2085
|
m_olas:{es:'Olas',en:'Waves'},
|
|
2080
2086
|
m_mar_calma:{es:'Mar en calma',en:'Calm sea'},
|
|
2087
|
+
/* Rev316: wave-hist-info popup */
|
|
2088
|
+
sh_wh_title:{es:'Historial de olas — leyenda y detalle',en:'Wave history — legend & details'},
|
|
2089
|
+
sh_wh_subtitle:{es:'Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.',en:'Each bar summarises 15 minutes. Color = average intensity measured by boat IMU. Bar height = bin oscillation.'},
|
|
2090
|
+
sh_wh_no_data:{es:'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.',en:'No data yet. Drop anchor and wait at least 15 min for bars to appear.'},
|
|
2091
|
+
sh_wh_inicio:{es:'Inicio',en:'Start'},
|
|
2092
|
+
sh_wh_intensidad:{es:'Intensidad',en:'Intensity'},
|
|
2093
|
+
sh_wh_periodo:{es:'Período',en:'Period'},
|
|
2094
|
+
sh_wh_altura:{es:'Altura',en:'Height'},
|
|
2095
|
+
sh_wh_rms:{es:'RMS',en:'RMS'},
|
|
2096
|
+
sh_wh_rizada:{es:'Rizada',en:'Rippled'},
|
|
2097
|
+
sh_wh_moderada:{es:'Moderada',en:'Moderate'},
|
|
2098
|
+
sh_wh_agitada:{es:'Agitada',en:'Rough'},
|
|
2099
|
+
sh_wh_fuerte:{es:'Fuerte',en:'Strong'},
|
|
2100
|
+
sh_wh_actual:{es:'ACTUAL',en:'CURRENT'},
|
|
2101
|
+
/* Rev316: tooltips left bar */
|
|
2102
|
+
lb_ham_tip:{es:'Menú',en:'Menu'},
|
|
2103
|
+
lb_audio_tip:{es:'Activar / desactivar audio',en:'Enable / disable audio'},
|
|
2104
|
+
lb_snooze_tip:{es:'Snooze 5 min',en:'Snooze 5 min'},
|
|
2105
|
+
lb_fav_tip:{es:'Fondeo favorito',en:'Favourite anchorage'},
|
|
2106
|
+
lb_mareas_tip:{es:'Mareas (TidesView)',en:'Tides (TidesView)'},
|
|
2107
|
+
lb_kip_tip:{es:'Abrir KIP dashboard',en:'Open KIP dashboard'},
|
|
2108
|
+
lb_fb_tip:{es:'Abrir Freeboard-SK',en:'Open Freeboard-SK'},
|
|
2109
|
+
/* Rev316: alarma meteo */
|
|
2110
|
+
alarma_meteo:{es:'⛈️ Mala condición climática',en:'⛈️ Bad weather conditions'},
|
|
2111
|
+
alarma_meteo_desc:{es:'Aviso si previsión 6h supera viento 25 kt u ola 1.5 m',en:'Warning if 6h forecast exceeds wind 25 kt or wave 1.5 m'},
|
|
2081
2112
|
/* Rev313: modal guardar favorito */
|
|
2082
2113
|
fav_guardar_title:{es:'❤ Guardar fondeo favorito',en:'❤ Save favourite anchorage'},
|
|
2083
2114
|
fav_guardar_hint:{es:'Nombre del favorito (vacío = nombre automático por geolocalización):',en:'Favourite name (empty = auto name by geolocation):'},
|
|
@@ -4852,25 +4883,32 @@ function _renderWaveHistInfo(){
|
|
|
4852
4883
|
var bins=Array.isArray(j.bins)?j.bins.slice():[];
|
|
4853
4884
|
if(j.currentBin)bins.push(j.currentBin);
|
|
4854
4885
|
if(bins.length===0){
|
|
4855
|
-
el.
|
|
4886
|
+
var _noDataMsg=(typeof T==='function')?T('sh_wh_no_data','No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.'):'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.';
|
|
4887
|
+
el.innerHTML='<div style="color:#888;text-align:center;padding:12px">'+_noDataMsg+'</div>';
|
|
4856
4888
|
return;
|
|
4857
4889
|
}
|
|
4858
|
-
/*
|
|
4859
|
-
var
|
|
4860
|
-
|
|
4890
|
+
/* Rev316: headers i18n. */
|
|
4891
|
+
var _tw=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
4892
|
+
var _hInicio=_tw('sh_wh_inicio','Inicio'), _hInten=_tw('sh_wh_intensidad','Intensidad'),
|
|
4893
|
+
_hPer=_tw('sh_wh_periodo','Período'), _hAlt=_tw('sh_wh_altura','Altura'), _hRms=_tw('sh_wh_rms','RMS');
|
|
4894
|
+
var _capCalma=_tw('m_calma','Calma'), _capRiz=_tw('sh_wh_rizada','Rizada'),
|
|
4895
|
+
_capMod=_tw('sh_wh_moderada','Moderada'), _capAgi=_tw('sh_wh_agitada','Agitada'), _capFue=_tw('sh_wh_fuerte','Fuerte');
|
|
4896
|
+
var _capMap={calma:_capCalma,rizada:_capRiz,moderada:_capMod,agitada:_capAgi,fuerte:_capFue};
|
|
4897
|
+
var _badgeActual=_tw('sh_wh_actual','ACTUAL');
|
|
4898
|
+
var rows=['<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:6px 0;border-bottom:1px solid rgba(255,255,255,.18);font-weight:800;color:#9ad;font-size:13px;letter-spacing:.8px;text-transform:uppercase">',
|
|
4899
|
+
'<span>'+_hInicio+'</span>','<span>'+_hInten+'</span>','<span style="text-align:right">'+_hPer+'</span>','<span style="text-align:right">'+_hAlt+'</span>','<span style="text-align:right">'+_hRms+'</span>',
|
|
4861
4900
|
'</div>'];
|
|
4862
|
-
/* Más recientes arriba */
|
|
4863
4901
|
var sorted=bins.slice().reverse();
|
|
4864
4902
|
for(var i=0;i<sorted.length;i++){
|
|
4865
4903
|
var b=sorted[i];
|
|
4866
4904
|
var d=new Date(b.binStartMs);
|
|
4867
4905
|
var hhmm=('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
|
|
4868
4906
|
var color=_WAVE_INT_COLORS[b.intensityMode]||'#888';
|
|
4869
|
-
var intCap=(b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1);
|
|
4907
|
+
var intCap=_capMap[b.intensityMode||'']||((b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1));
|
|
4870
4908
|
var per=(typeof b.periodAvgSec==='number'&&b.periodAvgSec>0)?b.periodAvgSec.toFixed(1)+'s':'—';
|
|
4871
4909
|
var alt=(typeof b.heightSigMaxM==='number'&&b.heightSigMaxM>0)?b.heightSigMaxM.toFixed(2).replace('.',',')+' m':'<5cm';
|
|
4872
4910
|
var rms=(typeof b.rmsMaxDeg==='number'&&b.rmsMaxDeg>0)?b.rmsMaxDeg.toFixed(2)+'°':'—';
|
|
4873
|
-
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">
|
|
4911
|
+
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">'+_badgeActual+'</span>':'';
|
|
4874
4912
|
rows.push('<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.06);align-items:center">'+
|
|
4875
4913
|
'<span style="color:#fff;font-weight:700">'+hhmm+'</span>'+
|
|
4876
4914
|
'<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:'+color+';margin-right:6px;vertical-align:middle"></span>'+intCap+curBadge+'</span>'+
|
|
@@ -6701,8 +6739,44 @@ function setAlarm(type,on){
|
|
|
6701
6739
|
document.getElementById('alarm-sonda-st').textContent=T('activa');document.getElementById('alarm-sonda-st').style.color='var(--grn)';
|
|
6702
6740
|
}
|
|
6703
6741
|
});
|
|
6742
|
+
}else if(type==='weather'){
|
|
6743
|
+
/* Rev316: alarma meteo — frontend-only por ahora. Estado en localStorage
|
|
6744
|
+
y check periódico contra _shelterCache.assessment.hours (próximas 6 h).
|
|
6745
|
+
Si viento > 25 kt u ola > 1.5 m → setAlarmActive('weather', true). */
|
|
6746
|
+
_alarmWeatherEnabled = on;
|
|
6747
|
+
localStorage.setItem('ihm-weather-alarm', on?'1':'0');
|
|
6748
|
+
var st = document.getElementById('alarm-weather-st');
|
|
6749
|
+
if (st) {
|
|
6750
|
+
st.textContent = on ? T('activa','activa') : T('desactivada','desactivada');
|
|
6751
|
+
st.style.color = on ? 'var(--grn)' : '#666';
|
|
6752
|
+
}
|
|
6753
|
+
if (!on) {
|
|
6754
|
+
try { setAlarmActive('weather', false); } catch(_){}
|
|
6755
|
+
} else {
|
|
6756
|
+
try { _checkWeatherAlarm(); } catch(_){}
|
|
6757
|
+
}
|
|
6704
6758
|
}
|
|
6705
6759
|
}
|
|
6760
|
+
/* Rev316: estado y comprobación de alarma meteo (frontend-only).
|
|
6761
|
+
Mira las próximas 6h de _shelterCache.assessment.hours buscando viento>25kt
|
|
6762
|
+
u ola>1.5m. Se invoca tras tocar el switch y cada 5 min mientras esté on. */
|
|
6763
|
+
var _alarmWeatherEnabled = (localStorage.getItem('ihm-weather-alarm')==='1');
|
|
6764
|
+
function _checkWeatherAlarm(){
|
|
6765
|
+
if (!_alarmWeatherEnabled) return;
|
|
6766
|
+
try {
|
|
6767
|
+
var asm = (typeof _shelterCache!=='undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
6768
|
+
if (!asm || !asm.hours || !asm.hours.length) return;
|
|
6769
|
+
var bad = false;
|
|
6770
|
+
for (var i=0; i<Math.min(6, asm.hours.length); i++){
|
|
6771
|
+
var hr = asm.hours[i];
|
|
6772
|
+
var w = hr.windKt || hr.exposureKt || 0;
|
|
6773
|
+
var wave = hr.waveM || 0;
|
|
6774
|
+
if (w > 25 || wave > 1.5) { bad = true; break; }
|
|
6775
|
+
}
|
|
6776
|
+
setAlarmActive('weather', bad);
|
|
6777
|
+
} catch(_){}
|
|
6778
|
+
}
|
|
6779
|
+
setInterval(_checkWeatherAlarm, 5*60*1000);
|
|
6706
6780
|
/* ══════════════ CALC SONDA ══════════════ */
|
|
6707
6781
|
/* ═══ SHARED INPUT STYLE (touch-friendly, wide for fingers) ═══ */
|
|
6708
6782
|
/* ═══ SHARED INPUT STYLE (touch-friendly) ═══ */
|
|
@@ -8110,13 +8184,17 @@ function m_syncBottomBarGraphs(){
|
|
|
8110
8184
|
var v = srcVal.textContent.trim();
|
|
8111
8185
|
if (v && v !== '—') dstVal.textContent = v;
|
|
8112
8186
|
}
|
|
8113
|
-
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo
|
|
8187
|
+
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo.
|
|
8188
|
+
Rev316: el % del donut leía un mapa hardcoded por letra (A=100, B=80, ...)
|
|
8189
|
+
que NO coincidía con el % real del shelter modal. Ahora lee el valor REAL
|
|
8190
|
+
(asm.scorePercent) del _shelterCache. Si no hay cache aún caemos al mapa
|
|
8191
|
+
como antes. */
|
|
8114
8192
|
var srcGrade = document.getElementById('wx-shelter-grade');
|
|
8115
8193
|
var dstGrade = document.getElementById('m-bb-grade');
|
|
8116
8194
|
var dstDonut = document.getElementById('m-bb-donut');
|
|
8117
8195
|
var dstDonutText = document.getElementById('m-bb-donut-text');
|
|
8118
8196
|
var gradeColors = { 'A':'#3aa856','B':'#9ed02e','C':'#ffb23f','D':'#ff7043','F':'#f44336' };
|
|
8119
|
-
var
|
|
8197
|
+
var donutPctFallback = { 'A':100, 'B':80, 'C':60, 'D':40, 'F':20 };
|
|
8120
8198
|
if (srcGrade && dstGrade){
|
|
8121
8199
|
var g = (srcGrade.textContent.trim() || '—').toUpperCase();
|
|
8122
8200
|
if (g && g !== '—' && g !== '?') {
|
|
@@ -8124,15 +8202,19 @@ function m_syncBottomBarGraphs(){
|
|
|
8124
8202
|
var bgColor = gradeColors[g] || 'rgba(120,120,120,.35)';
|
|
8125
8203
|
dstGrade.style.background = bgColor;
|
|
8126
8204
|
dstGrade.style.color = '#fff';
|
|
8205
|
+
/* Pct real del cache shelter; fallback al mapa por letra si no hay. */
|
|
8206
|
+
var realPct = null;
|
|
8207
|
+
try {
|
|
8208
|
+
var _asm = (typeof _shelterCache !== 'undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
8209
|
+
if (_asm && typeof _asm.scorePercent === 'number') realPct = Math.round(_asm.scorePercent);
|
|
8210
|
+
} catch(_){}
|
|
8211
|
+
if (realPct === null) realPct = donutPctFallback[g] || 0;
|
|
8127
8212
|
if (dstDonut) {
|
|
8128
|
-
|
|
8129
|
-
dstDonut.setAttribute('stroke-dasharray', pct + ',100');
|
|
8213
|
+
dstDonut.setAttribute('stroke-dasharray', realPct + ',100');
|
|
8130
8214
|
dstDonut.setAttribute('stroke', bgColor);
|
|
8131
8215
|
}
|
|
8132
|
-
/* Rev286: porcentaje (con %) dentro del donut, no letra. */
|
|
8133
8216
|
if (dstDonutText) {
|
|
8134
|
-
|
|
8135
|
-
dstDonutText.textContent = p + '%';
|
|
8217
|
+
dstDonutText.textContent = realPct + '%';
|
|
8136
8218
|
}
|
|
8137
8219
|
}
|
|
8138
8220
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-mareas-ihm",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Complete SignalK anchor watch & marine safety webapp. Voice alarms for anchor drag, AIS collision risk and grounding. Shelter / anchorage forecast (wind exposure + wave history). Weather forecast (Open-Meteo). Depth & scope calculator. Multi-source nautical charts (Esri Satellite, IHM ENC S-52, bathymetry, MBTiles offline, OpenStreetMap, OpenSeaMap). Favourite anchorages. Multi-device real-time sync (SSE). Includes official IHM Spain tide predictions. Mobile-first UI in ES/EN.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/public/mobile.html
CHANGED
|
@@ -1303,26 +1303,24 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1303
1303
|
<!-- Rev296: botones izquierdos dentro de #m-leftbar (scroll vertical, mismo
|
|
1304
1304
|
estilo que #m-sidebar derecha). -->
|
|
1305
1305
|
<div id="m-leftbar">
|
|
1306
|
-
<button id="m-ham" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1307
|
-
<button id="m-cartas" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1306
|
+
<button id="m-ham" data-i18n-title="lb_ham_tip" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1307
|
+
<button id="m-cartas" data-i18n-title="lb_audio_tip" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1308
1308
|
<span class="ico" id="m-mute-icon">🔊</span>
|
|
1309
1309
|
<span class="lbl" data-i18n="m_silencio">Silencio</span>
|
|
1310
1310
|
</button>
|
|
1311
|
-
<button id="m-snooze" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1311
|
+
<button id="m-snooze" data-i18n-title="lb_snooze_tip" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1312
1312
|
<span class="ico" id="m-snooze-icon">💤</span>
|
|
1313
1313
|
<span class="lbl" id="m-snooze-lbl" data-i18n="m_snooze">Snooze</span>
|
|
1314
1314
|
</button>
|
|
1315
|
-
<button id="m-fav" title="Fondeo favorito" onclick="m_favClick()">
|
|
1315
|
+
<button id="m-fav" data-i18n-title="lb_fav_tip" title="Fondeo favorito" onclick="m_favClick()">
|
|
1316
1316
|
<span class="ico">❤</span>
|
|
1317
1317
|
<span class="lbl" data-i18n="m_favorito">Favorito</span>
|
|
1318
1318
|
</button>
|
|
1319
|
-
|
|
1320
|
-
(entre Favorito y KIP). Mismos handlers que los del sidebar derecho. -->
|
|
1321
|
-
<button id="m-lb-curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1319
|
+
<button id="m-lb-curvas" data-i18n-title="sb_curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1322
1320
|
<span class="ico">〰️</span>
|
|
1323
1321
|
<span class="lbl" data-i18n="sb_curvas">Curvas</span>
|
|
1324
1322
|
</button>
|
|
1325
|
-
<button id="m-lb-mareas" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1323
|
+
<button id="m-lb-mareas" data-i18n-title="lb_mareas_tip" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1326
1324
|
<span class="ico" style="display:inline-flex;align-items:center;justify-content:center">
|
|
1327
1325
|
<svg viewBox="0 0 44 44" width="30" height="30">
|
|
1328
1326
|
<rect x="2" y="2" width="40" height="14" rx="2" fill="#ffd966"/>
|
|
@@ -1334,10 +1332,10 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1334
1332
|
</span>
|
|
1335
1333
|
<span class="lbl" data-i18n="sb_mareas">Mareas</span>
|
|
1336
1334
|
</button>
|
|
1337
|
-
<button id="m-kip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1335
|
+
<button id="m-kip" data-i18n-title="lb_kip_tip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1338
1336
|
<span class="ico" style="font-weight:900;letter-spacing:1px">KIP</span>
|
|
1339
1337
|
</button>
|
|
1340
|
-
<button id="m-fb" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1338
|
+
<button id="m-fb" data-i18n-title="lb_fb_tip" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1341
1339
|
<img src="assets/freeboard.png" alt="Freeboard"
|
|
1342
1340
|
style="width:42px;height:42px;object-fit:contain;background:#fff;border-radius:6px;padding:2px;display:block;box-sizing:border-box"/>
|
|
1343
1341
|
</button>
|
|
@@ -1389,7 +1387,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1389
1387
|
<span class="vl" id="m-bb-wind-kt-txt">—<span style="font-size:11px;font-weight:700;opacity:.75;margin-left:2px">kts</span></span>
|
|
1390
1388
|
</div>
|
|
1391
1389
|
</div>
|
|
1392
|
-
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop')"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1390
|
+
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop');typeof fetchSondaData==='function'&&fetchSondaData()"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1393
1391
|
<div class="it" data-i18n-title="bb_cadena_tip" title="Cadena recomendada" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_cadena_rec">Cadena rec.</span><span class="vl" id="m-bb-cad">—</span></div>
|
|
1394
1392
|
<div class="it" data-i18n-title="bb_dist_tip" title="Distancia al ancla" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_dist_ancla">Dist. ancla</span><span class="vl" id="m-bb-dist">—</span></div>
|
|
1395
1393
|
<div class="it m-bb-graph" data-i18n-title="bb_presion_tip" title="Presión barométrica · línea naranja = AHORA · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
|
|
@@ -1593,18 +1591,20 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1593
1591
|
</div>
|
|
1594
1592
|
<!-- Popup info histórico ola: leyenda colores + tabla detallada. -->
|
|
1595
1593
|
<div class="popup-overlay" id="wave-hist-info-pop" onclick="closePopup('wave-hist-info-pop')">
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
<
|
|
1600
|
-
<
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1604
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1605
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1594
|
+
<!-- Rev316: ventana wave-hist-info ampliada (antes 620px / 88vh) y todo
|
|
1595
|
+
bilingüe. Estilo título alineado con resto de modales (.m-modal-title). -->
|
|
1596
|
+
<div class="popup-box" onclick="event.stopPropagation()" style="width:900px;max-width:96vw;max-height:92vh;overflow-y:auto;padding:28px 32px">
|
|
1597
|
+
<button onclick="closePopup('wave-hist-info-pop')" style="position:absolute;top:10px;right:14px;background:none;border:none;color:#aaa;font-size:28px;cursor:pointer;line-height:1">✕</button>
|
|
1598
|
+
<h4 style="margin:0 0 12px 0;font-size:24px;font-weight:900;color:#fff" data-i18n="sh_wh_title">Historial de olas — leyenda y detalle</h4>
|
|
1599
|
+
<div style="font-size:16px;color:#9ad;margin-bottom:18px" data-i18n="sh_wh_subtitle">Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.</div>
|
|
1600
|
+
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:22px">
|
|
1601
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#3aa856;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="m_calma">Calma</div><div style="font-size:12px;color:#888"><0.5°</div></div>
|
|
1602
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#a3c813;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_rizada">Rizada</div><div style="font-size:12px;color:#888">0.5–1.5°</div></div>
|
|
1603
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f3c812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_moderada">Moderada</div><div style="font-size:12px;color:#888">1.5–3°</div></div>
|
|
1604
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f37812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_agitada">Agitada</div><div style="font-size:12px;color:#888">3–6°</div></div>
|
|
1605
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#e74c3c;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_fuerte">Fuerte</div><div style="font-size:12px;color:#888">>6°</div></div>
|
|
1606
1606
|
</div>
|
|
1607
|
-
<div id="wave-hist-info-table" style="font-size:
|
|
1607
|
+
<div id="wave-hist-info-table" style="font-size:15px;color:#cfd8dc"></div>
|
|
1608
1608
|
</div>
|
|
1609
1609
|
</div>
|
|
1610
1610
|
<!-- Rev79: popup explicacion del % de proteccion -->
|
|
@@ -1776,6 +1776,12 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1776
1776
|
<div class="alarm-info"><b style="color:var(--red)" data-i18n="alarma_sonda">🌊 Alarma Sonda</b><br><small style="color:#aaa" data-i18n="alarma_sonda_desc">Riesgo de varada por profundidad</small><div class="alarm-st" id="alarm-sonda-st"></div></div>
|
|
1777
1777
|
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-sonda" onclick="event.stopPropagation()" onchange="setAlarm('sonda',this.checked)"><label for="chk-alarm-sonda" onclick="event.stopPropagation()"></label></div>
|
|
1778
1778
|
</div>
|
|
1779
|
+
<!-- Rev316: nuevo selector "Mala condición climática" — alerta cuando
|
|
1780
|
+
la previsión meteo en las próximas 6h tiene viento > 25 kt o ola > 1.5 m. -->
|
|
1781
|
+
<div class="alarm-row" id="ar-weather" onclick="var c=document.getElementById('chk-alarm-weather');c.checked=!c.checked;setAlarm('weather',c.checked)">
|
|
1782
|
+
<div class="alarm-info"><b style="color:#ffb23f" data-i18n="alarma_meteo">⛈️ Mala condición climática</b><br><small style="color:#aaa" data-i18n="alarma_meteo_desc">Aviso si previsión 6h supera viento 25 kt u ola 1.5 m</small><div class="alarm-st" id="alarm-weather-st"></div></div>
|
|
1783
|
+
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-weather" onclick="event.stopPropagation()" onchange="setAlarm('weather',this.checked)"><label for="chk-alarm-weather" onclick="event.stopPropagation()"></label></div>
|
|
1784
|
+
</div>
|
|
1779
1785
|
</div>
|
|
1780
1786
|
<div id="alarm-active-list" style="margin-top:6px"></div>
|
|
1781
1787
|
<!-- Rev184: orden correcto — debajo de alarmas va el VOLUMEN, luego
|
|
@@ -2078,6 +2084,31 @@ var _i18n={
|
|
|
2078
2084
|
m_viento:{es:'Viento',en:'Wind'},
|
|
2079
2085
|
m_olas:{es:'Olas',en:'Waves'},
|
|
2080
2086
|
m_mar_calma:{es:'Mar en calma',en:'Calm sea'},
|
|
2087
|
+
/* Rev316: wave-hist-info popup */
|
|
2088
|
+
sh_wh_title:{es:'Historial de olas — leyenda y detalle',en:'Wave history — legend & details'},
|
|
2089
|
+
sh_wh_subtitle:{es:'Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.',en:'Each bar summarises 15 minutes. Color = average intensity measured by boat IMU. Bar height = bin oscillation.'},
|
|
2090
|
+
sh_wh_no_data:{es:'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.',en:'No data yet. Drop anchor and wait at least 15 min for bars to appear.'},
|
|
2091
|
+
sh_wh_inicio:{es:'Inicio',en:'Start'},
|
|
2092
|
+
sh_wh_intensidad:{es:'Intensidad',en:'Intensity'},
|
|
2093
|
+
sh_wh_periodo:{es:'Período',en:'Period'},
|
|
2094
|
+
sh_wh_altura:{es:'Altura',en:'Height'},
|
|
2095
|
+
sh_wh_rms:{es:'RMS',en:'RMS'},
|
|
2096
|
+
sh_wh_rizada:{es:'Rizada',en:'Rippled'},
|
|
2097
|
+
sh_wh_moderada:{es:'Moderada',en:'Moderate'},
|
|
2098
|
+
sh_wh_agitada:{es:'Agitada',en:'Rough'},
|
|
2099
|
+
sh_wh_fuerte:{es:'Fuerte',en:'Strong'},
|
|
2100
|
+
sh_wh_actual:{es:'ACTUAL',en:'CURRENT'},
|
|
2101
|
+
/* Rev316: tooltips left bar */
|
|
2102
|
+
lb_ham_tip:{es:'Menú',en:'Menu'},
|
|
2103
|
+
lb_audio_tip:{es:'Activar / desactivar audio',en:'Enable / disable audio'},
|
|
2104
|
+
lb_snooze_tip:{es:'Snooze 5 min',en:'Snooze 5 min'},
|
|
2105
|
+
lb_fav_tip:{es:'Fondeo favorito',en:'Favourite anchorage'},
|
|
2106
|
+
lb_mareas_tip:{es:'Mareas (TidesView)',en:'Tides (TidesView)'},
|
|
2107
|
+
lb_kip_tip:{es:'Abrir KIP dashboard',en:'Open KIP dashboard'},
|
|
2108
|
+
lb_fb_tip:{es:'Abrir Freeboard-SK',en:'Open Freeboard-SK'},
|
|
2109
|
+
/* Rev316: alarma meteo */
|
|
2110
|
+
alarma_meteo:{es:'⛈️ Mala condición climática',en:'⛈️ Bad weather conditions'},
|
|
2111
|
+
alarma_meteo_desc:{es:'Aviso si previsión 6h supera viento 25 kt u ola 1.5 m',en:'Warning if 6h forecast exceeds wind 25 kt or wave 1.5 m'},
|
|
2081
2112
|
/* Rev313: modal guardar favorito */
|
|
2082
2113
|
fav_guardar_title:{es:'❤ Guardar fondeo favorito',en:'❤ Save favourite anchorage'},
|
|
2083
2114
|
fav_guardar_hint:{es:'Nombre del favorito (vacío = nombre automático por geolocalización):',en:'Favourite name (empty = auto name by geolocation):'},
|
|
@@ -4852,25 +4883,32 @@ function _renderWaveHistInfo(){
|
|
|
4852
4883
|
var bins=Array.isArray(j.bins)?j.bins.slice():[];
|
|
4853
4884
|
if(j.currentBin)bins.push(j.currentBin);
|
|
4854
4885
|
if(bins.length===0){
|
|
4855
|
-
el.
|
|
4886
|
+
var _noDataMsg=(typeof T==='function')?T('sh_wh_no_data','No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.'):'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.';
|
|
4887
|
+
el.innerHTML='<div style="color:#888;text-align:center;padding:12px">'+_noDataMsg+'</div>';
|
|
4856
4888
|
return;
|
|
4857
4889
|
}
|
|
4858
|
-
/*
|
|
4859
|
-
var
|
|
4860
|
-
|
|
4890
|
+
/* Rev316: headers i18n. */
|
|
4891
|
+
var _tw=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
4892
|
+
var _hInicio=_tw('sh_wh_inicio','Inicio'), _hInten=_tw('sh_wh_intensidad','Intensidad'),
|
|
4893
|
+
_hPer=_tw('sh_wh_periodo','Período'), _hAlt=_tw('sh_wh_altura','Altura'), _hRms=_tw('sh_wh_rms','RMS');
|
|
4894
|
+
var _capCalma=_tw('m_calma','Calma'), _capRiz=_tw('sh_wh_rizada','Rizada'),
|
|
4895
|
+
_capMod=_tw('sh_wh_moderada','Moderada'), _capAgi=_tw('sh_wh_agitada','Agitada'), _capFue=_tw('sh_wh_fuerte','Fuerte');
|
|
4896
|
+
var _capMap={calma:_capCalma,rizada:_capRiz,moderada:_capMod,agitada:_capAgi,fuerte:_capFue};
|
|
4897
|
+
var _badgeActual=_tw('sh_wh_actual','ACTUAL');
|
|
4898
|
+
var rows=['<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:6px 0;border-bottom:1px solid rgba(255,255,255,.18);font-weight:800;color:#9ad;font-size:13px;letter-spacing:.8px;text-transform:uppercase">',
|
|
4899
|
+
'<span>'+_hInicio+'</span>','<span>'+_hInten+'</span>','<span style="text-align:right">'+_hPer+'</span>','<span style="text-align:right">'+_hAlt+'</span>','<span style="text-align:right">'+_hRms+'</span>',
|
|
4861
4900
|
'</div>'];
|
|
4862
|
-
/* Más recientes arriba */
|
|
4863
4901
|
var sorted=bins.slice().reverse();
|
|
4864
4902
|
for(var i=0;i<sorted.length;i++){
|
|
4865
4903
|
var b=sorted[i];
|
|
4866
4904
|
var d=new Date(b.binStartMs);
|
|
4867
4905
|
var hhmm=('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
|
|
4868
4906
|
var color=_WAVE_INT_COLORS[b.intensityMode]||'#888';
|
|
4869
|
-
var intCap=(b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1);
|
|
4907
|
+
var intCap=_capMap[b.intensityMode||'']||((b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1));
|
|
4870
4908
|
var per=(typeof b.periodAvgSec==='number'&&b.periodAvgSec>0)?b.periodAvgSec.toFixed(1)+'s':'—';
|
|
4871
4909
|
var alt=(typeof b.heightSigMaxM==='number'&&b.heightSigMaxM>0)?b.heightSigMaxM.toFixed(2).replace('.',',')+' m':'<5cm';
|
|
4872
4910
|
var rms=(typeof b.rmsMaxDeg==='number'&&b.rmsMaxDeg>0)?b.rmsMaxDeg.toFixed(2)+'°':'—';
|
|
4873
|
-
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">
|
|
4911
|
+
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">'+_badgeActual+'</span>':'';
|
|
4874
4912
|
rows.push('<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.06);align-items:center">'+
|
|
4875
4913
|
'<span style="color:#fff;font-weight:700">'+hhmm+'</span>'+
|
|
4876
4914
|
'<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:'+color+';margin-right:6px;vertical-align:middle"></span>'+intCap+curBadge+'</span>'+
|
|
@@ -6701,8 +6739,44 @@ function setAlarm(type,on){
|
|
|
6701
6739
|
document.getElementById('alarm-sonda-st').textContent=T('activa');document.getElementById('alarm-sonda-st').style.color='var(--grn)';
|
|
6702
6740
|
}
|
|
6703
6741
|
});
|
|
6742
|
+
}else if(type==='weather'){
|
|
6743
|
+
/* Rev316: alarma meteo — frontend-only por ahora. Estado en localStorage
|
|
6744
|
+
y check periódico contra _shelterCache.assessment.hours (próximas 6 h).
|
|
6745
|
+
Si viento > 25 kt u ola > 1.5 m → setAlarmActive('weather', true). */
|
|
6746
|
+
_alarmWeatherEnabled = on;
|
|
6747
|
+
localStorage.setItem('ihm-weather-alarm', on?'1':'0');
|
|
6748
|
+
var st = document.getElementById('alarm-weather-st');
|
|
6749
|
+
if (st) {
|
|
6750
|
+
st.textContent = on ? T('activa','activa') : T('desactivada','desactivada');
|
|
6751
|
+
st.style.color = on ? 'var(--grn)' : '#666';
|
|
6752
|
+
}
|
|
6753
|
+
if (!on) {
|
|
6754
|
+
try { setAlarmActive('weather', false); } catch(_){}
|
|
6755
|
+
} else {
|
|
6756
|
+
try { _checkWeatherAlarm(); } catch(_){}
|
|
6757
|
+
}
|
|
6704
6758
|
}
|
|
6705
6759
|
}
|
|
6760
|
+
/* Rev316: estado y comprobación de alarma meteo (frontend-only).
|
|
6761
|
+
Mira las próximas 6h de _shelterCache.assessment.hours buscando viento>25kt
|
|
6762
|
+
u ola>1.5m. Se invoca tras tocar el switch y cada 5 min mientras esté on. */
|
|
6763
|
+
var _alarmWeatherEnabled = (localStorage.getItem('ihm-weather-alarm')==='1');
|
|
6764
|
+
function _checkWeatherAlarm(){
|
|
6765
|
+
if (!_alarmWeatherEnabled) return;
|
|
6766
|
+
try {
|
|
6767
|
+
var asm = (typeof _shelterCache!=='undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
6768
|
+
if (!asm || !asm.hours || !asm.hours.length) return;
|
|
6769
|
+
var bad = false;
|
|
6770
|
+
for (var i=0; i<Math.min(6, asm.hours.length); i++){
|
|
6771
|
+
var hr = asm.hours[i];
|
|
6772
|
+
var w = hr.windKt || hr.exposureKt || 0;
|
|
6773
|
+
var wave = hr.waveM || 0;
|
|
6774
|
+
if (w > 25 || wave > 1.5) { bad = true; break; }
|
|
6775
|
+
}
|
|
6776
|
+
setAlarmActive('weather', bad);
|
|
6777
|
+
} catch(_){}
|
|
6778
|
+
}
|
|
6779
|
+
setInterval(_checkWeatherAlarm, 5*60*1000);
|
|
6706
6780
|
/* ══════════════ CALC SONDA ══════════════ */
|
|
6707
6781
|
/* ═══ SHARED INPUT STYLE (touch-friendly, wide for fingers) ═══ */
|
|
6708
6782
|
/* ═══ SHARED INPUT STYLE (touch-friendly) ═══ */
|
|
@@ -8110,13 +8184,17 @@ function m_syncBottomBarGraphs(){
|
|
|
8110
8184
|
var v = srcVal.textContent.trim();
|
|
8111
8185
|
if (v && v !== '—') dstVal.textContent = v;
|
|
8112
8186
|
}
|
|
8113
|
-
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo
|
|
8187
|
+
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo.
|
|
8188
|
+
Rev316: el % del donut leía un mapa hardcoded por letra (A=100, B=80, ...)
|
|
8189
|
+
que NO coincidía con el % real del shelter modal. Ahora lee el valor REAL
|
|
8190
|
+
(asm.scorePercent) del _shelterCache. Si no hay cache aún caemos al mapa
|
|
8191
|
+
como antes. */
|
|
8114
8192
|
var srcGrade = document.getElementById('wx-shelter-grade');
|
|
8115
8193
|
var dstGrade = document.getElementById('m-bb-grade');
|
|
8116
8194
|
var dstDonut = document.getElementById('m-bb-donut');
|
|
8117
8195
|
var dstDonutText = document.getElementById('m-bb-donut-text');
|
|
8118
8196
|
var gradeColors = { 'A':'#3aa856','B':'#9ed02e','C':'#ffb23f','D':'#ff7043','F':'#f44336' };
|
|
8119
|
-
var
|
|
8197
|
+
var donutPctFallback = { 'A':100, 'B':80, 'C':60, 'D':40, 'F':20 };
|
|
8120
8198
|
if (srcGrade && dstGrade){
|
|
8121
8199
|
var g = (srcGrade.textContent.trim() || '—').toUpperCase();
|
|
8122
8200
|
if (g && g !== '—' && g !== '?') {
|
|
@@ -8124,15 +8202,19 @@ function m_syncBottomBarGraphs(){
|
|
|
8124
8202
|
var bgColor = gradeColors[g] || 'rgba(120,120,120,.35)';
|
|
8125
8203
|
dstGrade.style.background = bgColor;
|
|
8126
8204
|
dstGrade.style.color = '#fff';
|
|
8205
|
+
/* Pct real del cache shelter; fallback al mapa por letra si no hay. */
|
|
8206
|
+
var realPct = null;
|
|
8207
|
+
try {
|
|
8208
|
+
var _asm = (typeof _shelterCache !== 'undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
8209
|
+
if (_asm && typeof _asm.scorePercent === 'number') realPct = Math.round(_asm.scorePercent);
|
|
8210
|
+
} catch(_){}
|
|
8211
|
+
if (realPct === null) realPct = donutPctFallback[g] || 0;
|
|
8127
8212
|
if (dstDonut) {
|
|
8128
|
-
|
|
8129
|
-
dstDonut.setAttribute('stroke-dasharray', pct + ',100');
|
|
8213
|
+
dstDonut.setAttribute('stroke-dasharray', realPct + ',100');
|
|
8130
8214
|
dstDonut.setAttribute('stroke', bgColor);
|
|
8131
8215
|
}
|
|
8132
|
-
/* Rev286: porcentaje (con %) dentro del donut, no letra. */
|
|
8133
8216
|
if (dstDonutText) {
|
|
8134
|
-
|
|
8135
|
-
dstDonutText.textContent = p + '%';
|
|
8217
|
+
dstDonutText.textContent = realPct + '%';
|
|
8136
8218
|
}
|
|
8137
8219
|
}
|
|
8138
8220
|
}
|