// GreenhouseOS - Ana JavaScript // Paylaşımlı fonksiyonlar footer.php'de: mqttConnect, mqttPublish, showNotification, updateLastUpdateTime // Bu dosya: navigasyon, sensör UI, tooltip, slider yardımcıları const API_BASE = 'api'; const UPDATE_INTERVAL = 30000; document.addEventListener('DOMContentLoaded', function() { initNavigation(); initRealTimeUpdates(); initTooltips(); initRangeSliders(); }); // ==================== NAVİGASYON ==================== function initNavigation() { var currentPage = window.location.pathname.split('/').pop() || 'index.php'; if (!currentPage) currentPage = 'index.php'; document.querySelectorAll('.nav-item').forEach(function(item) { var href = item.getAttribute('href'); if (href && href === currentPage) { item.classList.add('active'); } else if (href) { item.classList.remove('active'); } }); } // ==================== CANLI GÜNCELLEMELER ==================== function initRealTimeUpdates() { if (document.getElementById('tempValue')) { setInterval(fetchSensorData, UPDATE_INTERVAL); fetchSensorData(); } } function fetchSensorData() { fetch(API_BASE + '/sensors.php?latest=1&_t=' + Date.now(), { headers: {'Accept': 'application/json'} }) .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(function(data) { if (data.success && data.readings) updateSensorDisplays(data.readings); }) .catch(function(err) { console.warn('Sensör API:', err.message); }); } function updateSensorDisplays(readings) { if (!Array.isArray(readings)) return; var latest = {}; readings.forEach(function(r) { if (!latest[r.node_id] || new Date(r.created_at) > new Date(latest[r.node_id].created_at)) latest[r.node_id] = r; }); var temps=[],hums=[],soils=[],lights=[],phs=[],ecs=[],co2s=[]; Object.values(latest).forEach(function(r) { if (r.temperature) temps.push(parseFloat(r.temperature)); if (r.humidity) hums.push(parseFloat(r.humidity)); if (r.soil_moisture) soils.push(parseFloat(r.soil_moisture)); if (r.light_intensity)lights.push(parseFloat(r.light_intensity)); if (r.ph_value) phs.push(parseFloat(r.ph_value)); if (r.ec_value) ecs.push(parseFloat(r.ec_value)); if (r.co2_ppm) co2s.push(parseFloat(r.co2_ppm)); }); function avg(a){ return a.length?(a.reduce(function(x,y){return x+y;},0)/a.length).toFixed(1):null; } if (temps.length) updateValue('tempValue', avg(temps)+'°C'); if (hums.length) updateValue('humidityValue', Math.round(avg(hums))+'%'); if (soils.length) updateValue('soilValue', Math.round(avg(soils))+'%'); if (lights.length) updateValue('lightValue', Math.round(avg(lights)).toLocaleString()); if (phs.length) updateValue('phValue', avg(phs)); if (ecs.length) updateValue('ecValue', avg(ecs)); if (co2s.length) updateValue('co2Value', Math.round(avg(co2s))); } function updateValue(id, val) { var el = document.getElementById(id); if (!el) return; el.style.transition = 'opacity 0.2s'; el.style.opacity = '0.4'; setTimeout(function(){ el.textContent = val; el.style.opacity = '1'; }, 200); } // ==================== TOOLTIP ==================== function initTooltips() { document.querySelectorAll('[title]').forEach(function(el) { el.addEventListener('mouseenter', showTooltip); el.addEventListener('mouseleave', hideTooltip); }); } function showTooltip(e) { var title = e.currentTarget.getAttribute('title'); if (!title) return; var tip = document.createElement('div'); tip.className = 'custom-tooltip'; tip.textContent = title; tip.style.cssText = 'position:fixed;background:#16213e;color:#e0e6ed;padding:6px 12px;border-radius:6px;font-size:12px;z-index:9999;border:1px solid #2a3a5c;pointer-events:none;white-space:nowrap;transform:translateX(-50%)'; var rect = e.currentTarget.getBoundingClientRect(); tip.style.left = (rect.left + rect.width/2) + 'px'; tip.style.top = (rect.top - 35) + 'px'; document.body.appendChild(tip); e.currentTarget._tooltip = tip; } function hideTooltip(e) { if (e.currentTarget._tooltip) { e.currentTarget._tooltip.remove(); delete e.currentTarget._tooltip; } } // ==================== RANGE SLIDERLAR ==================== function initRangeSliders() { document.querySelectorAll('input[type="range"]').forEach(function(slider) { var display = slider.parentElement.querySelector('.duration-value, span:last-child'); if (!display) return; slider.addEventListener('input', function() { var unit = ''; var label = this.parentElement.querySelector('label'); if (label) { if (label.textContent.indexOf('dk') !== -1) unit = ' dk'; else if (label.textContent.indexOf('%') !== -1) unit = '%'; } display.textContent = this.value + unit; }); }); } // ==================== CHART PERIOD (index.php) ==================== function changeChartPeriod(period) { document.querySelectorAll('.chart-filter').forEach(function(b){ b.classList.remove('active'); }); if (event && event.target) event.target.classList.add('active'); var hours = period==='24h' ? 24 : period==='7d' ? 168 : 720; fetch(API_BASE + '/sensors.php?hours=' + hours) .then(function(r){ return r.json(); }) .then(function(d){ console.log('Periyot:', hours+'s,', d.count, 'kayıt'); }) .catch(function(){}); } window.onerror = function(msg, url, line) { console.error('JS Hata:', msg, 'Satır:', line); return false; }; window.addEventListener('unhandledrejection', function(e) { console.error('Promise Hata:', e.reason); });