// ===== Firebase config (from Firebase console web app) =====
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "your-project-id.firebaseapp.com",
  databaseURL: "https://your-project-id-default-rtdb.YOUR_REGION.firebasedatabase.app",
  projectId: "your-project-id",
};
 
const DEVICE_ID = "sparrow-01";
const MAX_CHART_POINTS = 50;
const CHART_LOCAL_SRC = "chart.umd.min.js?v=1";
const CHART_CDN_SRC   = "https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js";
 
// Wait for Firebase scripts (loaded via defer)
window.addEventListener("DOMContentLoaded", () => {
  firebase.initializeApp(firebaseConfig);
 
  const auth = firebase.auth();
  const db = firebase.database();
 
  const loginScreen = document.getElementById("login-screen");
  const dashboard   = document.getElementById("dashboard");
 
  const loginEmail    = document.getElementById("login-email");
  const loginPassword = document.getElementById("login-password");
  const loginBtn      = document.getElementById("login-btn");
  const loginError    = document.getElementById("login-error");
 
  const logoutBtn  = document.getElementById("logout-btn");
  const userEmail  = document.getElementById("user-email");
  const latestDiv  = document.getElementById("latest-reading");
  const historyBody = document.getElementById("history-body");
  const chartTempEl = document.getElementById("chart-temp");
  const chartHumEl  = document.getElementById("chart-hum");
  const chartPressEl= document.getElementById("chart-press");
  const chartStatus = document.getElementById("chart-status");
 
  const ledOnCheckbox = document.getElementById("led-on");
  const ledColorInput = document.getElementById("led-color");
  const ledApplyBtn   = document.getElementById("led-apply-btn");
  const ledStatus     = document.getElementById("led-status");
 
  let charts = null;
  const chartState = {
    labels: [],
    temp: [],
    hum: [],
    press: [],
  };
 
  function loadScript(src) {
    return new Promise((resolve, reject) => {
      const s = document.createElement("script");
      s.src = src;
      s.onload = resolve;
      s.onerror = reject;
      document.head.appendChild(s);
    });
  }
 
  async function ensureChartReady() {
    if (window.Chart) {
      if (chartStatus) chartStatus.textContent = `Charts ready (Chart.js ${window.Chart.version || ""})`;
      return true;
    }
    if (chartStatus) chartStatus.textContent = "Loading charts…";
    try {
      await loadScript(CHART_LOCAL_SRC);
    } catch (e) {
      console.warn("Local Chart.js failed, trying CDN", e);
      try {
        await loadScript(CHART_CDN_SRC);
      } catch (e2) {
        console.error("Chart.js failed to load from all sources", e2);
        return false;
      }
    }
    const ok = !!window.Chart;
    if (chartStatus) chartStatus.textContent = ok ? `Charts ready (Chart.js ${window.Chart.version || ""})` : "Charts unavailable";
    return ok;
  }
 
  function resetCharts() {
    chartState.labels = [];
    chartState.temp = [];
    chartState.hum = [];
    chartState.press = [];
    if (charts) {
      charts.temp.data.labels = [];
      charts.temp.data.datasets[0].data = [];
      charts.hum.data.labels = [];
      charts.hum.data.datasets[0].data = [];
      charts.press.data.labels = [];
      charts.press.data.datasets[0].data = [];
      charts.temp.update("none");
      charts.hum.update("none");
      charts.press.update("none");
    }
  }
 
  function initCharts() {
    if (!window.Chart) {
      console.warn("Chart.js not available");
      return;
    }
 
    const baseOptions = {
      responsive: true,
      animation: false,
      plugins: { legend: { display: false } },
      scales: {
        x: { ticks: { color: "#aaa" }, grid: { color: "rgba(255,255,255,0.05)" } },
        y: { ticks: { color: "#aaa" }, grid: { color: "rgba(255,255,255,0.05)" } },
      },
    };
 
    charts = {
      temp: new Chart(chartTempEl.getContext("2d"), {
        type: "line",
        data: { labels: [], datasets: [{ label: "Temp (°C)", data: [], borderColor: "#ff6b6b", backgroundColor: "rgba(255,107,107,0.15)", tension: 0.2, fill: true, pointRadius: 0 }] },
        options: baseOptions,
      }),
      hum: new Chart(chartHumEl.getContext("2d"), {
        type: "line",
        data: { labels: [], datasets: [{ label: "Hum (%)", data: [], borderColor: "#4fd1c5", backgroundColor: "rgba(79,209,197,0.15)", tension: 0.2, fill: true, pointRadius: 0 }] },
        options: baseOptions,
      }),
      press: new Chart(chartPressEl.getContext("2d"), {
        type: "line",
        data: { labels: [], datasets: [{ label: "Press (hPa)", data: [], borderColor: "#9f7aea", backgroundColor: "rgba(159,122,234,0.15)", tension: 0.2, fill: true, pointRadius: 0 }] },
        options: baseOptions,
      }),
    };
  }
 
  function updateCharts(ts, t, h, p) {
    if (!charts) return;
    const label = new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
 
    chartState.labels.push(label);
    chartState.temp.push(t);
    chartState.hum.push(h);
    chartState.press.push(p);
 
    if (chartState.labels.length > MAX_CHART_POINTS) {
      chartState.labels.shift();
      chartState.temp.shift();
      chartState.hum.shift();
      chartState.press.shift();
    }
 
    charts.temp.data.labels = chartState.labels.slice();
    charts.hum.data.labels = chartState.labels.slice();
    charts.press.data.labels = chartState.labels.slice();
 
    charts.temp.data.datasets[0].data = chartState.temp.slice();
    charts.hum.data.datasets[0].data  = chartState.hum.slice();
    charts.press.data.datasets[0].data= chartState.press.slice();
 
    charts.temp.update("none");
    charts.hum.update("none");
    charts.press.update("none");
    if (chartStatus) chartStatus.textContent = `Charts live — last update ${label}`;
  }
 
  // ===== Auth UI handling =====
  auth.onAuthStateChanged((user) => {
    if (user) {
      loginScreen.style.display = "none";
      dashboard.style.display = "block";
      userEmail.textContent = user.email || "(no email)";
      resetCharts();
      ensureChartReady().then((ok) => {
        if (ok) {
          initCharts();
        } else {
          console.warn("Charts disabled: Chart.js not available");
          if (chartStatus) chartStatus.textContent = "Charts disabled: Chart.js not available (check console / cache)";
        }
        startDataListeners();
      });
    } else {
      dashboard.style.display = "none";
      loginScreen.style.display = "block";
      userEmail.textContent = "";
      historyBody.innerHTML = "";
      latestDiv.innerHTML = "<p>No data yet…</p>";
      resetCharts();
      if (chartStatus) chartStatus.textContent = "Charts require login to load.";
    }
  });
 
  loginBtn.addEventListener("click", () => {
    loginError.textContent = "";
    const email = loginEmail.value.trim();
    const pass  = loginPassword.value.trim();
    auth.signInWithEmailAndPassword(email, pass)
      .catch(err => {
        console.error(err);
        loginError.textContent = err.message;
      });
  });
 
  logoutBtn.addEventListener("click", () => {
    auth.signOut();
  });
 
  // ===== Telemetry listeners =====
  function addHistoryRow(ts, t, h, p) {
    const tr = document.createElement("tr");
    const date = new Date(ts);
 
    tr.innerHTML = `
      <td>${date.toLocaleString()}</td>
      <td>${t.toFixed(1)}</td>
      <td>${h.toFixed(1)}</td>
      <td>${p.toFixed(1)}</td>
    `;
    historyBody.prepend(tr); // newest on top
 
    // Keep at most 20 rows
    while (historyBody.rows.length > 20) {
      historyBody.deleteRow(historyBody.rows.length - 1);
    }
  }
 
  function updateLatest(ts, t, h, p) {
    const date = new Date(ts);
    latestDiv.innerHTML = `
      <p><strong>${date.toLocaleString()}</strong></p>
      <p>Temperature: <strong>${t.toFixed(1)} °C</strong></p>
      <p>Humidity: <strong>${h.toFixed(1)} %</strong></p>
      <p>Pressure: <strong>${p.toFixed(1)} hPa</strong></p>
    `;
  }
 
  function startDataListeners() {
    // Listen for last 20 telemetry entries
    const telemRef = db.ref(`devices/${DEVICE_ID}/telemetry`).limitToLast(20);
    telemRef.off(); // avoid duplicates on re-login
 
    telemRef.on("child_added", (snap) => {
      const val = snap.val();
      if (!val) return;
      const ts = val.timestamp || Date.now();
      const t  = val.temperature || 0;
      const h  = val.humidity || 0;
      const p  = val.pressure || 0;
 
      updateLatest(ts, t, h, p);
      addHistoryRow(ts, t, h, p);
      updateCharts(ts, t, h, p);
    });
 
    // Keep dashboard LED controls in sync with DB
    const ledRef = db.ref(`devices/${DEVICE_ID}/led`);
    ledRef.off();
 
    ledRef.on("value", (snap) => {
      const val = snap.val();
      if (!val) return;
      const state = val.state || "off";
      const color = val.color || "#ffffff";
 
      ledOnCheckbox.checked = state === "on";
      ledColorInput.value   = color;
      ledStatus.textContent = `Current: ${state.toUpperCase()} ${color}`;
    });
  }
 
  // ===== LED control write =====
  ledApplyBtn.addEventListener("click", () => {
    const state = ledOnCheckbox.checked ? "on" : "off";
    const color = ledColorInput.value || "#ffffff";
 
    const ledRef = db.ref(`devices/${DEVICE_ID}/led`);
    ledRef.set({ state, color })
      .then(() => {
        ledStatus.textContent = `Set: ${state.toUpperCase()} ${color}`;
      })
      .catch(err => {
        console.error(err);
        ledStatus.textContent = `Error: ${err.message}`;
      });
  });
});
iothings/laboratoare/2025_code/lab9_3.txt · Last modified: 2025/11/23 12:53 by dan.tudose
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0