{"id":1471,"date":"2026-04-14T02:57:44","date_gmt":"2026-04-14T02:57:44","guid":{"rendered":"https:\/\/envautomation.com\/?page_id=1471"},"modified":"2026-04-14T08:37:17","modified_gmt":"2026-04-14T08:37:17","slug":"wbgt-temperature-record","status":"publish","type":"page","link":"https:\/\/envautomation.com\/?page_id=1471","title":{"rendered":"WBGT -Temperature  Record"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1471\" class=\"elementor elementor-1471\">\n\t\t\t\t<div class=\"elementor-element elementor-element-50d848b e-flex e-con-boxed e-con e-parent\" data-id=\"50d848b\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-220ccce elementor-widget elementor-widget-html\" data-id=\"220ccce\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- \u2705 WBGT-HEAT STRESS RECORD PAGE\r\n\u2705 Live API connected to wbgt1_daily.php\r\n\u2705 Uses wbgt_readings table\r\n\u2705 Fixed label: WBGT-Heat Stress\r\n\u2705 Daily chart + 5-minute reading table\r\n\u2705 PDF download included\r\nPaste into Elementor \u2192 HTML widget\r\n-->\r\n\r\n<link rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Poppins:wght@300;400;500;600;700;800&display=swap\"\/>\r\n\r\n<style>\r\n  :root{\r\n    --blue:#1573e6; --blue-d:#0e5ec0;\r\n    --bd:#e1e4ea; --txt:#1b1e23; --muted:#4b5563; --bg:#fff;\r\n    --g:#16a34a; --y:#f59e0b; --r:#dc2626;\r\n  }\r\n\r\n  body{font-family:Poppins,Arial,Helvetica,sans-serif;margin:0;background:#fff;color:var(--txt)}\r\n  h2{margin:20px 0;text-align:center;font-weight:900;letter-spacing:.3px}\r\n  .wrap{max-width:1200px;margin:0 auto;padding:0 12px 32px}\r\n\r\n  .form{\r\n    display:grid;\r\n    grid-template-columns:minmax(160px,1fr) minmax(160px,1fr) min-content min-content;\r\n    gap:10px;\r\n    align-items:end;\r\n    max-width:760px;\r\n    margin:0 auto 10px;\r\n  }\r\n\r\n  .field{display:flex;flex-direction:column;gap:6px;min-width:0}\r\n  .label{font-size:12px;color:#111;font-weight:700;line-height:1.2}\r\n\r\n  .input{\r\n    padding:8px 10px;\r\n    border:1px solid #cfd6e1;\r\n    border-radius:8px;\r\n    font-size:13px;\r\n    background:#fff;\r\n    min-width:0;\r\n    height:38px;\r\n    font-family:Poppins,Arial,Helvetica,sans-serif;\r\n  }\r\n\r\n  .btn{\r\n    padding:8px 14px;\r\n    height:38px;\r\n    background:var(--blue);\r\n    color:#fff;\r\n    border:0;\r\n    border-radius:8px;\r\n    font-size:14px;\r\n    font-weight:800;\r\n    cursor:pointer;\r\n    white-space:nowrap;\r\n  }\r\n  .btn:hover{background:var(--blue-d)}\r\n\r\n  .btn-ghost{\r\n    padding:8px 14px;\r\n    height:38px;\r\n    background:#fff;\r\n    color:var(--blue);\r\n    border:1px solid var(--blue);\r\n    border-radius:8px;\r\n    cursor:pointer;\r\n    font-weight:800;\r\n    white-space:nowrap;\r\n  }\r\n  .btn-ghost:hover{background:#f0f6ff}\r\n\r\n  #reportWrap{display:none}\r\n  .meta-line{\r\n    display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin:10px 0 12px;color:var(--muted);font-weight:800\r\n  }\r\n  .meta-line .kv{display:flex;gap:6px}\r\n  .meta-line b{color:#111}\r\n\r\n  .table-card{\r\n    background:#fff;border:1px solid var(--bd);border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden\r\n  }\r\n  .table-scroller{overflow:auto;max-height:70vh}\r\n\r\n  table{border-collapse:separate;border-spacing:0;width:max-content;min-width:100%}\r\n  thead th{\r\n    position:sticky;top:0;background:#f8fafc;border-bottom:1px solid #eef2f7;padding:10px 8px;font-size:12px;text-align:center;z-index:2\r\n  }\r\n  tbody td, tbody th{\r\n    border-bottom:1px solid #eef2f7;border-right:1px solid #f1f5f9;padding:7px 6px;font-size:11px;text-align:center\r\n  }\r\n  tbody th.date{\r\n    position:sticky;left:0;background:#f8fafc;border-right:1px solid var(--bd);z-index:1\r\n  }\r\n  tbody th.time{\r\n    position:sticky;left:90px;background:#f8fafc;border-right:1px solid #eef2f7;z-index:1\r\n  }\r\n\r\n  .col-date{width:90px;min-width:90px}\r\n  .col-time{width:76px;min-width:76px}\r\n  .col-min{width:52px;min-width:52px}\r\n  .col-max{width:130px;min-width:130px;font-weight:900}\r\n\r\n  .g{background:#e9f7ee}\r\n  .y{background:#fff5db}\r\n  .r{background:#ffe6e6}\r\n\r\n  #chartSection{display:none;margin-top:14px;}\r\n  .chart-card{\r\n    background:#fff;\r\n    border:1px solid var(--bd);\r\n    border-radius:12px;\r\n    box-shadow:0 2px 8px rgba(0,0,0,.06);\r\n    padding:14px 18px 18px;\r\n  }\r\n  .chart-box{height:260px;position:relative;}\r\n  #wbgtChart{width:100% !important;height:100% !important;display:block;}\r\n\r\n  #pdfHeaderWrap{width:100%;background:#fff;}\r\n  .pdf-header{\r\n    display:flex;align-items:center;gap:14px;\r\n    padding:10px 0 8px;\r\n    border-bottom:2px solid #e8eef6;\r\n  }\r\n  .pdf-logo{width:64px;height:64px;object-fit:contain;flex:0 0 auto}\r\n  .pdf-head-text{line-height:1.1}\r\n  .pdf-co{font-weight:900;font-size:20px;letter-spacing:.4px;color:#0b2a5a}\r\n  .pdf-sub{font-size:11px;color:#334155;margin-top:4px}\r\n  .pdf-sub a{color:#0b2a5a;text-decoration:underline}\r\n  .pdf-title{\r\n    margin:12px 0 8px;\r\n    font-size:16px;\r\n    font-weight:900;\r\n    letter-spacing:.6px;\r\n    text-transform:uppercase;\r\n  }\r\n  .pdf-meta{\r\n    width:100%;\r\n    border-collapse:separate;\r\n    border-spacing:0;\r\n    border:1px solid #d8dee9;\r\n    border-radius:10px;\r\n    overflow:hidden;\r\n    margin-bottom:12px;\r\n  }\r\n  .pdf-meta th, .pdf-meta td{\r\n    padding:10px 10px;\r\n    border-bottom:1px solid #e7edf6;\r\n    border-right:1px solid #e7edf6;\r\n    font-size:12px;\r\n    text-align:left;\r\n    vertical-align:middle;\r\n  }\r\n  .pdf-meta tr:last-child th, .pdf-meta tr:last-child td{border-bottom:0}\r\n  .pdf-meta th{width:240px;background:#f8fafc;font-weight:900;color:#111827}\r\n  .pdf-meta td{font-weight:700;color:#111827;border-right:0}\r\n  .pdf-only{position:absolute;left:-99999px;top:-99999px;width:980px;}\r\n\r\n  .legend-note{\r\n    display:flex;flex-wrap:wrap;gap:10px;margin:10px 0 0;font-size:12px;font-weight:700;color:#425466\r\n  }\r\n  .legend-chip{\r\n    display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;border:1px solid #dbe5ef;background:#fff\r\n  }\r\n  .legend-dot{width:10px;height:10px;border-radius:999px}\r\n  .lg-g{background:#16a34a}\r\n  .lg-y{background:#f59e0b}\r\n  .lg-r{background:#dc2626}\r\n\r\n  @media (max-width:980px){\r\n    .form{\r\n      grid-template-columns:1fr 1fr;\r\n      max-width:100%;\r\n    }\r\n    .form button{\r\n      grid-column:1 \/ -1;\r\n      width:100%;\r\n    }\r\n    .chart-box{height:220px;}\r\n  }\r\n\r\n  @media (max-width:640px){\r\n    .form{grid-template-columns:1fr;}\r\n  }\r\n<\/style>\r\n\r\n<h2>WBGT-HEAT STRESS RECORD<\/h2>\r\n\r\n<div class=\"wrap\">\r\n  <form id=\"filterForm\" class=\"form\" autocomplete=\"off\">\r\n    <div class=\"field\">\r\n      <label class=\"label\" for=\"fromDate\">From Date<\/label>\r\n      <input id=\"fromDate\" class=\"input\" type=\"date\" required\/>\r\n    <\/div>\r\n\r\n    <div class=\"field\">\r\n      <label class=\"label\" for=\"toDate\">To Date<\/label>\r\n      <input id=\"toDate\" class=\"input\" type=\"date\" required\/>\r\n    <\/div>\r\n\r\n    <button type=\"submit\" class=\"btn\">Search<\/button>\r\n    <button type=\"button\" id=\"btnDownload\" class=\"btn-ghost\">Download<\/button>\r\n  <\/form>\r\n\r\n  <div id=\"chartSection\">\r\n    <div class=\"chart-card\" id=\"chartCard\">\r\n      <div class=\"chart-box\">\r\n        <canvas id=\"wbgtChart\"><\/canvas>\r\n      <\/div>\r\n\r\n      <div class=\"legend-note\">\r\n        <span class=\"legend-chip\"><span class=\"legend-dot lg-g\"><\/span>Below 31 = Low Heat Stress<\/span>\r\n        <span class=\"legend-chip\"><span class=\"legend-dot lg-y\"><\/span>31 to &lt;33 = Moderate Heat Stress<\/span>\r\n        <span class=\"legend-chip\"><span class=\"legend-dot lg-r\"><\/span>33 and above = High Heat Stress<\/span>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div id=\"reportWrap\">\r\n    <div class=\"meta-line\">\r\n      <span class=\"kv\">Project: <b id=\"metaProj\">\u2014<\/b><\/span>\r\n      <span class=\"kv\">Label: <b id=\"metaLabel\">WBGT-Heat Stress<\/b><\/span>\r\n      <span class=\"kv\">Metric: <b id=\"metaMetric\">\u2014<\/b><\/span>\r\n      <span class=\"kv\">Range: <b id=\"metaRange\">\u2014<\/b><\/span>\r\n      <span class=\"kv\">Records: <b id=\"metaCount\">0<\/b><\/span>\r\n      <span class=\"kv\">Days: <b id=\"metaDays\">0<\/b><\/span>\r\n      <span class=\"kv\">Last updated: <b id=\"metaUpdated\">\u2014<\/b><\/span>\r\n    <\/div>\r\n\r\n    <div class=\"table-card\" id=\"pdfBlock\">\r\n      <div class=\"table-scroller\">\r\n        <table id=\"wbgtTable\" aria-label=\"WBGT 5-min Table\"><\/table>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div id=\"pdfHeaderWrap\" class=\"pdf-only\">\r\n    <div class=\"pdf-header\">\r\n      <img decoding=\"async\" class=\"pdf-logo\" id=\"pdfLogo\" src=\"https:\/\/envautomation.com\/wp-content\/uploads\/2025\/12\/cropped-envlogo-1.jpg\" alt=\"ENV Logo\" crossorigin=\"anonymous\">\r\n      <div class=\"pdf-head-text\">\r\n        <div class=\"pdf-co\">ENV AUTOMATION PTE LTD<\/div>\r\n        <div class=\"pdf-sub\">60 Paya Lebar Road, #07-54 Paya Lebar Square, Singapore 409051<\/div>\r\n        <div class=\"pdf-sub\">\r\n          Email: <a href=\"mailto:envautomation.com.sg@gmail.com\">envautomation.com.sg@gmail.com<\/a> |\r\n          website: <a href=\"https:\/\/www.autosiltstopper.com\" target=\"_blank\" rel=\"noopener\">www.autosiltstopper.com<\/a>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"pdf-title\" id=\"pdfTitle\">WBGT-HEAT STRESS RECORD<\/div>\r\n\r\n    <table class=\"pdf-meta\" id=\"pdfMetaTable\">\r\n      <tr><th>Company Name<\/th><td id=\"pdfCompany\">KIMLY<\/td><\/tr>\r\n      <tr><th>Project Name<\/th><td id=\"pdfProject\">\u2014<\/td><\/tr>\r\n      <tr><th>Label<\/th><td id=\"pdfLabel\">WBGT-Heat Stress<\/td><\/tr>\r\n      <tr><th>Metric<\/th><td id=\"pdfMetric\">WBGT (\u00b0C)<\/td><\/tr>\r\n      <tr><th>From Date<\/th><td id=\"pdfFrom\">\u2014<\/td><\/tr>\r\n      <tr><th>To Date<\/th><td id=\"pdfTo\">\u2014<\/td><\/tr>\r\n    <\/table>\r\n  <\/div>\r\n<\/div>\r\n\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/html2canvas@1.4.1\/dist\/html2canvas.min.js\"><\/script>\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf@2.5.1\/dist\/jspdf.umd.min.js\"><\/script>\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.1\/dist\/chart.umd.min.js\"><\/script>\r\n\r\n<script>\r\n\/* =========================\r\n   \u2705 WBGT PROJECT SETTINGS\r\n   ========================= *\/\r\nvar PROJECT_NAME = \"KIMLY KBGC3\";\r\nvar FIXED_LABEL = \"WBGT-Heat Stress\";\r\nvar SENSOR_ID = 1;\r\nvar API_BASE = \"https:\/\/api.envautomation.com\/dtu_ingest\/ass_kimlykbgc3\/wbgt1\";\r\n\r\nfunction buildDailyUrl(dateStr){\r\n  return API_BASE + \"\/wbgt1_daily.php?sensor_id=\" + encodeURIComponent(SENSOR_ID) + \"&date=\" + encodeURIComponent(dateStr);\r\n}\r\n\r\n\/* ===== Utils ===== *\/\r\nfunction pad(n){ return String(n).padStart(2,\"0\"); }\r\nfunction ymdLocal(d){ return d.getFullYear()+\"-\"+pad(d.getMonth()+1)+\"-\"+pad(d.getDate()); }\r\nfunction todayLocal(){ var d=new Date(); return new Date(d.getFullYear(), d.getMonth(), d.getDate()); }\r\nfunction addDays(d, n){ var x=new Date(d.getTime()); x.setDate(x.getDate()+n); return x; }\r\nfunction hourLabel24(h){ return pad(h)+\":00\"; }\r\nfunction labelFromYmd(ymd){ var p=ymd.split(\"-\"); return p[2]+\"\/\"+p[1]+\"\/\"+p[0]; }\r\n\r\nfunction emptyDay(){\r\n  var map = {};\r\n  for(var h=0; h<24; h++){\r\n    for(var m=5; m<=55; m+=5){\r\n      map[pad(h)+\":\"+pad(m)] = null;\r\n    }\r\n  }\r\n  return map;\r\n}\r\n\r\n\/* ===== WBGT config ===== *\/\r\nfunction metricMeta(){\r\n  return {\r\n    name:\"WBGT\",\r\n    unit:\"\u00b0C\",\r\n    decimals:1,\r\n    yMin:26,\r\n    yMax:40,\r\n    line30:30,\r\n    line31:31,\r\n    line33:33,\r\n    title:\"WBGT-HEAT STRESS RECORD\"\r\n  };\r\n}\r\n\r\n\/* \u2705 COLOR BANDS *\/\r\nfunction bandClassByWBGT(v){\r\n  if(v==null || isNaN(v)) return \"\";\r\n  v = Number(v);\r\n  if(v < 31) return \"g\";\r\n  if(v < 33) return \"y\";\r\n  return \"r\";\r\n}\r\n\r\n\/* ===== Daily parser ===== *\/\r\nfunction normalizeDailyWBGT(json){\r\n  var grid = emptyDay();\r\n  var seen = {};\r\n\r\n  if(json && json.readings && Object.prototype.toString.call(json.readings) === \"[object Array]\"){\r\n    for(var i=0;i<json.readings.length;i++){\r\n      var r = json.readings[i];\r\n      var timeStr = (r.time || \"\");\r\n      var parts = timeStr.split(\":\");\r\n      if(parts.length < 2) continue;\r\n\r\n      var H = parseInt(parts[0],10);\r\n      var Mraw = parseInt(parts[1],10);\r\n      if(isNaN(H) || isNaN(Mraw)) continue;\r\n\r\n      var bucket = (Mraw < 5) ? 5 : Math.floor(Mraw\/5)*5;\r\n      if(bucket > 55) bucket = 55;\r\n\r\n      var key = pad(H)+\":\"+pad(bucket);\r\n\r\n      var wbgt = (typeof r.wbgt !== \"undefined\" && r.wbgt !== null && !isNaN(Number(r.wbgt))) ? Number(r.wbgt) : null;\r\n      if(wbgt!=null){\r\n        grid[key] = wbgt;\r\n        seen[key] = true;\r\n      }\r\n    }\r\n  }\r\n  return {grid:grid, count:Object.keys(seen).length};\r\n}\r\n\r\nfunction dailyMaxFromGrid(grid){\r\n  var maxVal = null;\r\n  for(var h=0; h<24; h++){\r\n    var hh = pad(h);\r\n    for(var m5=5; m5<=55; m5+=5){\r\n      var key = hh+\":\"+pad(m5);\r\n      var v = grid[key];\r\n      if(v!=null && !isNaN(v)){\r\n        if(maxVal===null) maxVal = Number(v);\r\n        else if(v > maxVal) maxVal = Number(v);\r\n      }\r\n    }\r\n  }\r\n  return (maxVal===null) ? null : Number(maxVal);\r\n}\r\n\r\n\/* ===== Table renderer ===== *\/\r\nfunction renderWBGTTable(container, daysData){\r\n  var meta = metricMeta();\r\n  var times = [];\r\n  for(var m=5; m<=55; m+=5){ times.push(pad(m)); }\r\n\r\n  var html = \"<thead><tr><th class='col-date'>Date<\/th><th class='col-time'>Time<\/th>\";\r\n  for(var t=0; t<times.length; t++){ html += \"<th class='col-min'>\"+times[t]+\"<\/th>\"; }\r\n  html += \"<th class='col-max'>WBGT (Max) \"+meta.unit+\"<\/th><\/tr><\/thead><tbody>\";\r\n\r\n  for(var d=0; d<daysData.length; d++){\r\n    var day = daysData[d];\r\n    var parts = day.date.split(\"-\");\r\n    var dateLabel = parts[2]+\"-\"+parts[1]+\"-\"+parts[0];\r\n    var grid = day.grid;\r\n\r\n    for(var h=0; h<24; h++){\r\n      var hh = pad(h);\r\n      var maxVal = null;\r\n\r\n      html += \"<tr><th class='date col-date date'>\"+dateLabel+\"<\/th><th class='time col-time time'>\"+hourLabel24(h)+\"<\/th>\";\r\n\r\n      for(var m5=5; m5<=55; m5+=5){\r\n        var key = hh+\":\"+pad(m5);\r\n        var val = grid[key];\r\n\r\n        if(val!=null){\r\n          if(maxVal===null) maxVal = Number(val);\r\n          else if(val > maxVal) maxVal = Number(val);\r\n        }\r\n\r\n        var cls = bandClassByWBGT(val);\r\n        html += \"<td class='col-min \"+cls+\"'>\"+(val==null ? \"\u2014\" : Number(val).toFixed(meta.decimals))+\"<\/td>\";\r\n      }\r\n\r\n      var maxCls = bandClassByWBGT(maxVal);\r\n      html += \"<td class='col-max \"+maxCls+\"'>\"+(maxVal==null ? \"\u2014\" : Number(maxVal).toFixed(meta.decimals))+\"<\/td><\/tr>\";\r\n    }\r\n  }\r\n\r\n  html += \"<\/tbody>\";\r\n  container.innerHTML = html;\r\n}\r\n\r\n\/* ===== Chart ===== *\/\r\nvar chartInstance = null;\r\n\r\nfunction renderChart(labels, dailyMaxes){\r\n  var chartSection = document.getElementById(\"chartSection\");\r\n  var chartCanvas  = document.getElementById(\"wbgtChart\");\r\n  var meta = metricMeta();\r\n\r\n  if(!labels || labels.length === 0){\r\n    chartSection.style.display = \"block\";\r\n    labels = [\"No Data\"];\r\n    dailyMaxes = [null];\r\n  } else {\r\n    chartSection.style.display = \"block\";\r\n  }\r\n\r\n  var ctx = chartCanvas.getContext(\"2d\");\r\n\r\n  var datasets = [\r\n    {\r\n      label: 'WBGT-Heat Stress (' + meta.unit + ')',\r\n      data: dailyMaxes,\r\n      borderColor:'#000000',\r\n      backgroundColor:'#000000',\r\n      pointRadius:4,\r\n      borderWidth:2,\r\n      tension:0,\r\n      spanGaps:true\r\n    },\r\n    {\r\n      label:'30\u00b0C Green Line',\r\n      data: labels.map(function(){ return meta.line30; }),\r\n      borderColor:'#16a34a',\r\n      borderDash:[6,6],\r\n      borderWidth:2,\r\n      pointRadius:0\r\n    },\r\n    {\r\n      label:'31\u00b0C Yellow Line',\r\n      data: labels.map(function(){ return meta.line31; }),\r\n      borderColor:'#f59e0b',\r\n      borderDash:[6,6],\r\n      borderWidth:2,\r\n      pointRadius:0\r\n    },\r\n    {\r\n      label:'33\u00b0C Red Line',\r\n      data: labels.map(function(){ return meta.line33; }),\r\n      borderColor:'#dc2626',\r\n      borderDash:[6,6],\r\n      borderWidth:2,\r\n      pointRadius:0\r\n    }\r\n  ];\r\n\r\n  var config = {\r\n    type: 'line',\r\n    data: { labels: labels, datasets: datasets },\r\n    options: {\r\n      responsive:true,\r\n      maintainAspectRatio:false,\r\n      plugins:{\r\n        legend:{display:true, position:'top'},\r\n        title:{display:true, text:'WBGT-Heat Stress Reading', font:{size:16}}\r\n      },\r\n      scales:{\r\n        y:{\r\n          min:meta.yMin,\r\n          max:meta.yMax,\r\n          title:{display:true, text:'WBGT (' + meta.unit + ')'},\r\n          ticks:{ stepSize:1 }\r\n        },\r\n        x:{\r\n          title:{display:true, text:'Date'},\r\n          ticks:{autoSkip:false, maxRotation:60, minRotation:60, font:{size:10}}\r\n        }\r\n      }\r\n    }\r\n  };\r\n\r\n  if(chartInstance){\r\n    chartInstance.data = config.data;\r\n    chartInstance.options = config.options;\r\n    chartInstance.update();\r\n  }else{\r\n    chartInstance = new Chart(ctx, config);\r\n  }\r\n}\r\n\r\n\/* ===== PDF helpers ===== *\/\r\nfunction canvasToPngWithWhiteBg(canvas){\r\n  var tmp = document.createElement(\"canvas\");\r\n  tmp.width = canvas.width;\r\n  tmp.height = canvas.height;\r\n  var ctx = tmp.getContext(\"2d\");\r\n  ctx.fillStyle = \"#ffffff\";\r\n  ctx.fillRect(0,0,tmp.width,tmp.height);\r\n  ctx.drawImage(canvas,0,0);\r\n  return tmp.toDataURL(\"image\/png\");\r\n}\r\n\r\nfunction drawLegendPills(pdf, x, y){\r\n  var pillH = 8, gap = 4;\r\n\r\n  function pill(fillRgb, textColorRgb, left, right){\r\n    pdf.setFillColor.apply(pdf, fillRgb);\r\n    pdf.setTextColor.apply(pdf, textColorRgb);\r\n    pdf.roundedRect(x, y, 22, pillH, 4, 4, 'F');\r\n    pdf.setFont('helvetica','bold');\r\n    pdf.setFontSize(8.8);\r\n    pdf.text(left, x+3.5, y+5.5);\r\n\r\n    pdf.setTextColor(17,24,39);\r\n    pdf.setFont('helvetica','normal');\r\n    pdf.setFontSize(10);\r\n    pdf.text(right, x+26, y+5.8);\r\n    y += pillH + gap;\r\n  }\r\n\r\n  pill([22,163,74], [255,255,255], \"<31\", \"Low heat stress\");\r\n  pill([245,158,11], [17,24,39], \"31-32.9\", \"Moderate heat stress\");\r\n  pill([220,38,38], [255,255,255], \">=33\", \"High heat stress\");\r\n\r\n  return y;\r\n}\r\n\r\n\/* ===== DOM ===== *\/\r\nvar frm, fromDateI, toDateI, btnDownload;\r\nvar reportWrap;\r\nvar metaProj, metaLabel, metaMetric, metaRange, metaCount, metaDays, metaUpdated;\r\nvar tableEl, pdfBlock;\r\nvar pdfProject, pdfLabel, pdfFrom, pdfTo, pdfMetric, pdfTitle;\r\n\r\nvar autoTimer = null;\r\n\r\nfunction syncPdfMeta(from, to){\r\n  var meta = metricMeta();\r\n  pdfLabel.textContent   = FIXED_LABEL;\r\n  pdfFrom.textContent    = from || \"\u2014\";\r\n  pdfTo.textContent      = to || \"\u2014\";\r\n  pdfMetric.textContent  = meta.name + \" (\" + meta.unit + \")\";\r\n  pdfTitle.textContent   = meta.title;\r\n}\r\n\r\n\/* ===== Search ===== *\/\r\nfunction runSearch(){\r\n  var from = fromDateI.value;\r\n  var to   = toDateI.value;\r\n  if(!from || !to) return;\r\n\r\n  if(from > to){\r\n    alert(\"From Date cannot be after To Date.\");\r\n    return;\r\n  }\r\n\r\n  var meta = metricMeta();\r\n\r\n  reportWrap.style.display = \"block\";\r\n\r\n  metaRange.textContent  = from+\" \u2192 \"+to;\r\n  metaLabel.textContent  = FIXED_LABEL;\r\n  metaMetric.textContent = meta.name + \" (\" + meta.unit + \")\";\r\n\r\n  syncPdfMeta(from, to);\r\n\r\n  var d   = new Date(from+\"T00:00:00\");\r\n  var end = new Date(to+\"T00:00:00\");\r\n\r\n  var daysData  = [];\r\n  var totalRecs = 0;\r\n  var labels    = [];\r\n  var dailyMaxes= [];\r\n  var latestApiTime = \"\u2014\";\r\n\r\n  function loopOneDay(){\r\n    if(d > end){\r\n      metaCount.textContent   = String(totalRecs);\r\n      metaDays.textContent    = String(daysData.length);\r\n      metaUpdated.textContent = latestApiTime !== \"\u2014\" ? latestApiTime : new Date().toLocaleString(\"en-SG\",{hour12:false});\r\n\r\n      renderWBGTTable(tableEl, daysData);\r\n      renderChart(labels, dailyMaxes);\r\n      return;\r\n    }\r\n\r\n    var ymdStr = ymdLocal(d);\r\n    var url = buildDailyUrl(ymdStr);\r\n\r\n    fetch(url, {cache:\"no-store\"})\r\n      .then(function(res){ if(!res.ok) throw new Error(\"HTTP \"+res.status); return res.json(); })\r\n      .then(function(json){\r\n        var result = normalizeDailyWBGT(json);\r\n        daysData.push({date: ymdStr, grid: result.grid});\r\n        totalRecs += result.count;\r\n\r\n        labels.push(labelFromYmd(ymdStr));\r\n        var mx = dailyMaxFromGrid(result.grid);\r\n        dailyMaxes.push(mx==null ? null : mx);\r\n\r\n        if(json && Array.isArray(json.readings) && json.readings.length){\r\n          var lastRow = json.readings[json.readings.length - 1];\r\n          latestApiTime = ymdStr + \" \" + (lastRow.time || \"00:00:00\");\r\n        }\r\n\r\n        d = addDays(d, 1);\r\n        loopOneDay();\r\n      })\r\n      .catch(function(err){\r\n        console.warn(\"Daily fetch failed:\", url, err);\r\n        daysData.push({date: ymdStr, grid: emptyDay()});\r\n        labels.push(labelFromYmd(ymdStr));\r\n        dailyMaxes.push(null);\r\n        d = addDays(d, 1);\r\n        loopOneDay();\r\n      });\r\n  }\r\n  loopOneDay();\r\n}\r\n\r\n\/* ===== INIT ===== *\/\r\ndocument.addEventListener(\"DOMContentLoaded\", function(){\r\n  frm          = document.getElementById(\"filterForm\");\r\n  fromDateI    = document.getElementById(\"fromDate\");\r\n  toDateI      = document.getElementById(\"toDate\");\r\n  btnDownload  = document.getElementById(\"btnDownload\");\r\n  reportWrap   = document.getElementById(\"reportWrap\");\r\n\r\n  metaProj     = document.getElementById(\"metaProj\");\r\n  metaLabel    = document.getElementById(\"metaLabel\");\r\n  metaMetric   = document.getElementById(\"metaMetric\");\r\n  metaRange    = document.getElementById(\"metaRange\");\r\n  metaCount    = document.getElementById(\"metaCount\");\r\n  metaDays     = document.getElementById(\"metaDays\");\r\n  metaUpdated  = document.getElementById(\"metaUpdated\");\r\n\r\n  tableEl      = document.getElementById(\"wbgtTable\");\r\n  pdfBlock     = document.getElementById(\"pdfBlock\");\r\n\r\n  pdfProject   = document.getElementById(\"pdfProject\");\r\n  pdfLabel     = document.getElementById(\"pdfLabel\");\r\n  pdfFrom      = document.getElementById(\"pdfFrom\");\r\n  pdfTo        = document.getElementById(\"pdfTo\");\r\n  pdfMetric    = document.getElementById(\"pdfMetric\");\r\n  pdfTitle     = document.getElementById(\"pdfTitle\");\r\n\r\n  metaProj.textContent = PROJECT_NAME;\r\n  pdfProject.textContent = PROJECT_NAME;\r\n  metaLabel.textContent = FIXED_LABEL;\r\n  pdfLabel.textContent = FIXED_LABEL;\r\n\r\n  var t = ymdLocal(todayLocal());\r\n  fromDateI.value = t;\r\n  toDateI.value   = t;\r\n\r\n  frm.addEventListener(\"submit\", function(e){\r\n    e.preventDefault();\r\n    runSearch();\r\n    if(autoTimer) clearInterval(autoTimer);\r\n    autoTimer = setInterval(runSearch, 5*60*1000);\r\n  });\r\n\r\n  runSearch();\r\n  if(autoTimer) clearInterval(autoTimer);\r\n  autoTimer = setInterval(runSearch, 5*60*1000);\r\n\r\n  btnDownload.addEventListener(\"click\", async function(){\r\n    if(fromDateI.value > toDateI.value){\r\n      alert(\"From Date cannot be after To Date.\");\r\n      return;\r\n    }\r\n\r\n    var jsPDF = window.jspdf && window.jspdf.jsPDF;\r\n    if(!jsPDF){ alert(\"jsPDF not loaded.\"); return; }\r\n\r\n    btnDownload.disabled = true;\r\n    var oldTxt = btnDownload.textContent;\r\n    btnDownload.textContent = \"Preparing...\";\r\n\r\n    try{\r\n      var PDF_SCALE = 2;\r\n      var from = fromDateI.value;\r\n      var to   = toDateI.value;\r\n\r\n      syncPdfMeta(from, to);\r\n\r\n      document.getElementById(\"chartSection\").style.display = \"block\";\r\n      if(chartInstance) chartInstance.update();\r\n      await new Promise(function(r){ setTimeout(r, 180); });\r\n\r\n      var headerEl = document.getElementById(\"pdfHeaderWrap\");\r\n      var headerCanvas = await html2canvas(headerEl, {\r\n        scale: PDF_SCALE,\r\n        backgroundColor: '#ffffff',\r\n        useCORS: true\r\n      });\r\n      var headerImg = canvasToPngWithWhiteBg(headerCanvas);\r\n\r\n      var chartEl = document.getElementById(\"chartCard\");\r\n      var chartCanvasCap = await html2canvas(chartEl, {\r\n        scale: PDF_SCALE,\r\n        backgroundColor: '#ffffff',\r\n        useCORS: true\r\n      });\r\n      var chartImg = canvasToPngWithWhiteBg(chartCanvasCap);\r\n\r\n      var scroller = pdfBlock.querySelector('.table-scroller');\r\n      var oldOverflow = scroller.style.overflow;\r\n      var oldMaxH = scroller.style.maxHeight;\r\n\r\n      scroller.style.overflow = \"visible\";\r\n      scroller.style.maxHeight = \"none\";\r\n\r\n      await new Promise(function(r){ setTimeout(r, 80); });\r\n\r\n      var tableCanvas = await html2canvas(pdfBlock, {\r\n        scale: PDF_SCALE,\r\n        backgroundColor: '#ffffff',\r\n        useCORS: true\r\n      });\r\n      var tableImg = canvasToPngWithWhiteBg(tableCanvas);\r\n\r\n      scroller.style.overflow = oldOverflow;\r\n      scroller.style.maxHeight = oldMaxH;\r\n\r\n      var pdf = new jsPDF(\"p\",\"mm\",\"a4\");\r\n      var pageW = pdf.internal.pageSize.getWidth();\r\n      var pageH = pdf.internal.pageSize.getHeight();\r\n      var margin = 10;\r\n      var y = margin;\r\n\r\n      var headerW = pageW - margin*2;\r\n      var headerH = headerW * (headerCanvas.height \/ headerCanvas.width);\r\n      pdf.addImage(headerImg, \"PNG\", margin, y, headerW, headerH);\r\n      y += headerH + 6;\r\n\r\n      y = drawLegendPills(pdf, margin, y) + 2;\r\n\r\n      var chartW = pageW - margin*2;\r\n      var chartH = chartW * (chartCanvasCap.height \/ chartCanvasCap.width);\r\n      if(y + chartH > pageH - margin){\r\n        pdf.addPage(); y = margin;\r\n      }\r\n      pdf.addImage(chartImg, \"PNG\", margin, y, chartW, chartH);\r\n      y += chartH + 6;\r\n\r\n      var imgProps = pdf.getImageProperties(tableImg);\r\n      var tableW = pageW - margin*2;\r\n      var tableH = tableW * (imgProps.height \/ imgProps.width);\r\n\r\n      if(y + tableH <= pageH - margin){\r\n        pdf.addImage(tableImg, \"PNG\", margin, y, tableW, tableH);\r\n      }else{\r\n        var remaining = tableH;\r\n        var offsetY = 0;\r\n\r\n        while(remaining > 0){\r\n          var space = pageH - margin - y;\r\n          pdf.addImage(tableImg, \"PNG\", margin, y - offsetY, tableW, tableH);\r\n\r\n          remaining -= space;\r\n          offsetY += space;\r\n\r\n          if(remaining > 0){\r\n            pdf.addPage();\r\n            y = margin;\r\n          }\r\n        }\r\n      }\r\n\r\n      var filename = PROJECT_NAME + \"_WBGT-HeatStress_\" + from + \"_to_\" + to + \".pdf\";\r\n      pdf.save(filename);\r\n\r\n    }catch(err){\r\n      console.error(err);\r\n      alert(\"PDF failed. Please try again.\");\r\n    }finally{\r\n      btnDownload.disabled = false;\r\n      btnDownload.textContent = oldTxt;\r\n    }\r\n  });\r\n});\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>WBGT-HEAT STRESS RECORD From Date To Date Search Download Below 31 = Low Heat Stress 31 to &lt;33 = Moderate [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-1471","page","type-page","status-publish","hentry"],"brizy_media":[],"_links":{"self":[{"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/pages\/1471","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/envautomation.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1471"}],"version-history":[{"count":5,"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/pages\/1471\/revisions"}],"predecessor-version":[{"id":1495,"href":"https:\/\/envautomation.com\/index.php?rest_route=\/wp\/v2\/pages\/1471\/revisions\/1495"}],"wp:attachment":[{"href":"https:\/\/envautomation.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1471"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}