🌿 GROWLINGER 3.5 PRO

GROWLINGER

Professional Grow Journal • ML Predictions • IoT Ready

Online
v3.5 PRO
ML lädt…

📊 Live Umgebungsdaten

Temperatur

–°C
Warte…

Luftfeuchte

–%
Warte…

VPD

kPa

Datenpunkte

0
erfasst

📈 Trends (24h)

🤖 ML Vorhersagen

Modell lädt…

📸 Zeitraffer & Foto-Dokumentation

📷
Kamera starten um Fotos aufzunehmen

📷 Foto-Galerie (0 Fotos)

🧬 Strain-Datenbank

➕ Eigenen Strain hinzufügen

🧮 Nährstoff-Rechner

📊 pH & EC Management

💰 Kosten & ROI Tracker

➕ Kosten hinzufügen

📊 Kosten-Übersicht

💎 ROI Berechnung

📊 3D Grow Room

🎮 Erfolge & Fortschritt

🏆
0 XP
Level 1 Grower

🏅 Erfolge

📝
📊 Dash
📸 Foto
🧬 DNA
🧮 Calc
📊 3D
`); },// STRAIN FUNCTIONS loadStrains() { this.strains = [...this.strainDatabase]; this.renderStrains(); },searchStrains(query) { const filtered = this.strains.filter(s => s.name.toLowerCase().includes(query.toLowerCase()) || s.type.toLowerCase().includes(query.toLowerCase()) ); this.renderStrains(filtered); },renderStrains(list = this.strains) { const container = document.getElementById('strainList'); if (list.length === 0) { container.innerHTML = '
Keine Strains gefunden
'; return; } container.innerHTML = list.map(s => `
${s.name}
${s.desc || ''}
${s.type}
THC: ${s.thc}%
CBD: ${s.cbd}%
Blüte: ${s.flowering}d
Ertrag: ${s.yield}g/m²
`).join(''); },selectStrain(name) { const strain = this.strains.find(s => s.name === name); if (!strain) return; const confirm = window.confirm(`🧬 ${strain.name} auswählen?\n\n${strain.desc}\n\nTyp: ${strain.type}\nTHC: ${strain.thc}%\nBlütezeit: ${strain.flowering} Tage`); if (confirm) { this.currentStrain = strain; this.save(); this.addXP(50, `🧬 Strain gewählt: ${strain.name}`); alert(`✅ ${strain.name} als aktueller Strain gesetzt!`); } },addCustomStrain() { const name = document.getElementById('customStrainName').value.trim(); const type = document.getElementById('customType').value; const thc = parseFloat(document.getElementById('customThc').value) || 0; if (!name) { alert('❌ Bitte Strain-Namen eingeben!'); return; } const strain = { name, type, thc, cbd: 0.1, flowering: 60, yield: 400, desc: 'Custom Strain', custom: true }; this.strains.push(strain); this.renderStrains(); this.save(); this.addXP(30, '➕ Eigener Strain hinzugefügt'); document.getElementById('customStrainName').value = ''; document.getElementById('customThc').value = ''; alert(`✅ ${name} zur Datenbank hinzugefügt!`); },// NUTRIENT FUNCTIONS calculateNutrients() { const water = parseFloat(document.getElementById('waterAmount').value) || 10; const phase = document.getElementById('nutrientPhase').value; const brand = document.getElementById('nutrientBrand').value; const recipe = this.nutrientRecipes[brand][phase]; let html = '
'; html += `

📋 ${brand.toUpperCase()} - ${phase.toUpperCase()}

`; html += `
`; Object.keys(recipe).forEach(nutrient => { const mlPerLiter = recipe[nutrient]; const totalMl = (mlPerLiter * water).toFixed(1); html += `
`; html += `${nutrient}:`; html += `${totalMl} ml (${mlPerLiter} ml/L)`; html += `
`; }); html += `
`; html += `
`; html += `💡 Tipp: Nährstoffe in der Reihenfolge zugeben: Mikro → Gro → Bloom`; html += `
`; html += `
`; document.getElementById('nutrientResult').innerHTML = html; this.addXP(15, '🧪 Nährstoffe berechnet'); },checkPhEc() { const ph = parseFloat(document.getElementById('currentPh').value); const ec = parseFloat(document.getElementById('currentEc').value); if (!ph || !ec) { alert('❌ Bitte beide Werte eingeben!'); return; } let html = ''; // pH Check if (ph < 5.5) { html += `
⚠️ pH zu niedrig (${ph})! Ziel: 5.8-6.5
➜ pH+ hinzufügen
`; } else if (ph > 6.8) { html += `
⚠️ pH zu hoch (${ph})! Ziel: 5.8-6.5
➜ pH- hinzufügen
`; } else if (ph >= 5.8 && ph <= 6.5) { html += `
✅ pH optimal (${ph})
`; } else { html += `
⚠️ pH suboptimal (${ph}). Ziel: 5.8-6.5
`; } // EC Check if (ec < 0.8) { html += `
⚠️ EC niedrig (${ec} mS/cm)
➜ Mehr Nährstoffe
`; } else if (ec > 2.5) { html += `
⚠️ EC zu hoch (${ec} mS/cm)!
➜ Mit Wasser verdünnen
`; } else if (ec >= 1.0 && ec <= 2.0) { html += `
✅ EC optimal (${ec} mS/cm)
`; } else { html += `
ℹ️ EC: ${ec} mS/cm (akzeptabel)
`; } document.getElementById('phEcResult').innerHTML = html; this.addXP(10, '📊 pH/EC geprüft'); },// COST FUNCTIONS addCost() { const category = document.getElementById('costCategory').value; const amount = parseFloat(document.getElementById('costAmount').value); const description = document.getElementById('costDescription').value.trim(); if (!amount || amount <= 0) { alert('❌ Bitte gültigen Betrag eingeben!'); return; } const cost = { id: Date.now(), category, amount, description, date: new Date().toISOString() }; this.costs.push(cost); this.renderCosts(); this.save(); this.addXP(5, '💰 Kosten eingetragen'); document.getElementById('costAmount').value = ''; document.getElementById('costDescription').value = ''; alert(`✅ ${amount.toFixed(2)}€ eingetragen!`); },renderCosts() { const summary = document.getElementById('costSummary'); const list = document.getElementById('costList'); const total = this.costs.reduce((sum, c) => sum + c.amount, 0); const categories = { electricity: { name: 'Strom', icon: '💡', total: 0 }, water: { name: 'Wasser', icon: '💧', total: 0 }, nutrients: { name: 'Nährstoffe', icon: '🌿', total: 0 }, substrate: { name: 'Substrate', icon: '🌱', total: 0 }, equipment: { name: 'Equipment', icon: '🔧', total: 0 }, seeds: { name: 'Seeds', icon: '🌰', total: 0 }, other: { name: 'Sonstiges', icon: '📦', total: 0 } }; this.costs.forEach(c => { if (categories[c.category]) { categories[c.category].total += c.amount; } }); summary.innerHTML = Object.values(categories).map(cat => `

${cat.icon} ${cat.name}

${cat.total.toFixed(2)}€
`).join(''); if (this.costs.length === 0) { list.innerHTML = '
Keine Kosten eingetragen
'; return; } list.innerHTML = `
Gesamtkosten ${total.toFixed(2)}€
${this.costs.slice().reverse().slice(0, 10).map(c => `
${categories[c.category]?.name || c.category} ${c.description ? `
${c.description}
` : ''}
${new Date(c.date).toLocaleDateString('de-DE')}
${c.amount.toFixed(2)}€
`).join('')} `; },calculateROI() { const yieldG = parseFloat(document.getElementById('expectedYield').value); const pricePerG = parseFloat(document.getElementById('streetPrice').value); if (!yieldG || !pricePerG) { alert('❌ Bitte alle Felder ausfüllen!'); return; } const totalCosts = this.costs.reduce((sum, c) => sum + c.amount, 0); const revenue = yieldG * pricePerG; const profit = revenue - totalCosts; const roi = totalCosts > 0 ? (profit / totalCosts * 100) : 0; const html = `

💎 ROI Analyse

Kosten
${totalCosts.toFixed(2)}€
Wert
${revenue.toFixed(2)}€
Gewinn / ROI
${profit > 0 ? '+' : ''}${profit.toFixed(2)}€
${roi > 0 ? '+' : ''}${roi.toFixed(1)}%
${yieldG}g × ${pricePerG}€/g = ${revenue.toFixed(2)}€
Kosten pro Gramm: ${(totalCosts / yieldG).toFixed(2)}€/g
`; document.getElementById('roiResult').innerHTML = html; this.addXP(20, '💰 ROI berechnet'); },// 3D VIEW FUNCTIONS init3DView() { if (this.renderer3d) return; // Already initialized const container = document.getElementById('threejs-container'); const canvas = document.getElementById('canvas3d'); // Scene this.scene3d = new THREE.Scene(); this.scene3d.background = new THREE.Color(0x0a0f1a); // Camera this.camera3d = new THREE.PerspectiveCamera(75, container.offsetWidth / container.offsetHeight, 0.1, 1000); this.camera3d.position.set(5, 5, 5); this.camera3d.lookAt(0, 0, 0); // Renderer this.renderer3d = new THREE.WebGLRenderer({ canvas, antialias: true }); this.renderer3d.setSize(container.offsetWidth, container.offsetHeight); // Lights const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); this.scene3d.add(ambientLight); this.growLight = new THREE.PointLight(0x22c55e, 1, 100); this.growLight.position.set(0, 5, 0); this.scene3d.add(this.growLight); // Grow Tent (Room) const roomGeometry = new THREE.BoxGeometry(8, 6, 8); const roomMaterial = new THREE.MeshBasicMaterial({ color: 0x1e293b, wireframe: true, transparent: true, opacity: 0.3 }); const room = new THREE.Mesh(roomGeometry, roomMaterial); room.position.y = 3; this.scene3d.add(room); // Floor const floorGeometry = new THREE.PlaneGeometry(8, 8); const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x334155 }); const floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.rotation.x = -Math.PI / 2; this.scene3d.add(floor); // Plants for (let i = 0; i < 4; i++) { const plantGroup = new THREE.Group(); // Pot const potGeometry = new THREE.CylinderGeometry(0.3, 0.2, 0.4, 8); const potMaterial = new THREE.MeshStandardMaterial({ color: 0x8b4513 }); const pot = new THREE.Mesh(potGeometry, potMaterial); pot.position.y = 0.2; plantGroup.add(pot); // Plant const plantGeometry = new THREE.SphereGeometry(0.4, 8, 8); const plantMaterial = new THREE.MeshStandardMaterial({ color: 0x22c55e }); const plant = new THREE.Mesh(plantGeometry, plantMaterial); plant.position.y = 0.8; plant.scale.y = 1.5; plantGroup.add(plant); // Position plants in grid const x = (i % 2) * 2 - 1; const z = Math.floor(i / 2) * 2 - 1; plantGroup.position.set(x * 1.5, 0, z * 1.5); this.scene3d.add(plantGroup); } // Sensor const sensorGeometry = new THREE.BoxGeometry(0.2, 0.2, 0.1); const sensorMaterial = new THREE.MeshStandardMaterial({ color: 0x3b82f6 }); this.sensor3d = new THREE.Mesh(sensorGeometry, sensorMaterial); this.sensor3d.position.set(2, 1, 2); this.scene3d.add(this.sensor3d); // Animation this.animate3D(); // Handle resize window.addEventListener('resize', () => { if (this.camera3d && this.renderer3d) { this.camera3d.aspect = container.offsetWidth / container.offsetHeight; this.camera3d.updateProjectionMatrix(); this.renderer3d.setSize(container.offsetWidth, container.offsetHeight); } }); this.rotation3DActive = true; },animate3D() { if (!this.renderer3d) return; requestAnimationFrame(() => this.animate3D()); if (this.rotation3DActive) { this.camera3d.position.x = Math.cos(Date.now() * 0.0005) * 7; this.camera3d.position.z = Math.sin(Date.now() * 0.0005) * 7; this.camera3d.lookAt(0, 2, 0); } // Pulsing sensor if (this.sensor3d) { this.sensor3d.material.emissive = new THREE.Color(0x3b82f6); this.sensor3d.material.emissiveIntensity = 0.5 + Math.sin(Date.now() * 0.005) * 0.5; } this.renderer3d.render(this.scene3d, this.camera3d); },toggle3DRotation() { this.rotation3DActive = !this.rotation3DActive; const btn = document.getElementById('rotate3dBtn'); btn.textContent = this.rotation3DActive ? '⏸️ Pause' : '▶️ Rotation'; },toggle3DLight() { if (!this.growLight) return; this.growLight.visible = !this.growLight.visible; const btn = document.getElementById('toggle3dLightBtn'); btn.textContent = this.growLight.visible ? '💡 Licht aus' : '🌑 Licht an'; },// ACHIEVEMENT SYSTEM initAchievements() { this.achievements = [ { id: 'first_photo', name: 'Erster Schnappschuss', desc: 'Erstes Foto aufgenommen', icon: '📸', unlocked: false, xp: 50 }, { id: 'sensor_master', name: 'Sensor-Meister', desc: '100 Datenpunkte gesammelt', icon: '🛰️', unlocked: false, xp: 100 }, { id: 'strain_collector', name: 'Strain-Sammler', desc: '5 Strains in Datenbank', icon: '🧬', unlocked: false, xp: 75 }, { id: 'cost_tracker', name: 'Buchhalter', desc: '10 Kosten eingetragen', icon: '💰', unlocked: false, xp: 50 }, { id: 'week_streak', name: 'Wöchentlicher Check', desc: '7 Tage Streak', icon: '🔥', unlocked: false, xp: 150 }, { id: 'ml_expert', name: 'KI-Experte', desc: '10 ML Predictions', icon: '🤖', unlocked: false, xp: 200 }, { id: 'photo_album', name: 'Fotograf', desc: '25 Fotos aufgenommen', icon: '📷', unlocked: false, xp: 150 }, { id: 'nutrient_pro', name: 'Nährstoff-Profi', desc: '20 Berechnungen', icon: '🧪', unlocked: false, xp: 100 } ]; this.renderAchievements(); this.updateXPDisplay(); },renderAchievements() { const container = document.getElementById('achievementsList'); container.innerHTML = this.achievements.map(a => `
${a.icon}
${a.name}
${a.desc}
${a.unlocked ? '
✅ Freigeschaltet
' : '
🔒 Gesperrt
'}
+${a.xp} XP
`).join(''); },addXP(amount, reason) { this.xp += amount; const oldLevel = this.level; this.level = Math.floor(1 + this.xp / 500); this.updateXPDisplay(); this.save(); if (this.level > oldLevel) { this.showNotification(`🎉 Level ${this.level} erreicht!`, 'success'); } // Check achievements this.checkAchievements(); },updateXPDisplay() { document.getElementById('totalXP').textContent = this.xp + ' XP'; document.getElementById('userLevel').textContent = this.level; const xpForNextLevel = this.level * 500; const xpInCurrentLevel = this.xp % 500; const progress = (xpInCurrentLevel / 500) * 100; document.getElementById('xpBar').style.width = progress + '%'; },checkAchievements() { // First photo if (!this.achievements[0].unlocked && this.photos.length >= 1) { this.unlockAchievement('first_photo'); } // Sensor master if (!this.achievements[1].unlocked && this.sensorData.temps.length >= 100) { this.unlockAchievement('sensor_master'); } // Strain collector if (!this.achievements[2].unlocked && this.strains.filter(s => s.custom).length >= 5) { this.unlockAchievement('strain_collector'); } // Cost tracker if (!this.achievements[3].unlocked && this.costs.length >= 10) { this.unlockAchievement('cost_tracker'); } // Photo album if (!this.achievements[6].unlocked && this.photos.length >= 25) { this.unlockAchievement('photo_album'); } },unlockAchievement(id) { const achievement = this.achievements.find(a => a.id === id); if (!achievement || achievement.unlocked) return; achievement.unlocked = true; this.xp += achievement.xp; this.updateXPDisplay(); this.renderAchievements(); this.save(); this.showNotification(`🏆 Erfolg freigeschaltet: ${achievement.name}!`, 'success'); },checkDailyStreak() { const lastVisit = localStorage.getItem('growlinger_last_visit'); const today = new Date().toDateString(); if (lastVisit !== today) { const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); if (lastVisit === yesterday.toDateString()) { this.streak++; this.addXP(10, 'Täglicher Besuch'); } else { this.streak = 1; } localStorage.setItem('growlinger_last_visit', today); this.save(); } },showNotification(message, type = 'info') { const div = document.createElement('div'); div.className = `alert alert-${type}`; div.textContent = message; div.style.position = 'fixed'; div.style.top = '20px'; div.style.right = '20px'; div.style.zIndex = '9999'; div.style.animation = 'slideIn 0.3s ease-out'; document.body.appendChild(div); setTimeout(() => { div.style.animation = 'slideOut 0.3s ease-in'; setTimeout(() => div.remove(), 300); }, 3000); },fabMenu() { const actions = ['📸 Schnellfoto', '📝 Notiz', '💧 Gießen', '🌿 Düngen', '⚠️ Problem']; const choice = prompt(`Schnellaktionen:\n\n${actions.map((a,i) => `${i+1}. ${a}`).join('\n')}\n\nWähle (1-5):`); if (choice && choice >= 1 && choice <= 5) { const action = actions[parseInt(choice)-1]; if (choice == 1) { document.querySelector('[data-tab="camera"]').click(); } else { this.addXP(5, action); alert(`✅ ${action}\n\n📅 ${new Date().toLocaleString('de-DE')}`); } } },save() { try { const data = { sensorData: this.sensorData, photos: this.photos.slice(-50).map(p => ({ ...p, data: p.data?.substring(0, 50000) })), costs: this.costs, strains: this.strains.filter(s => s.custom), achievements: this.achievements, xp: this.xp, level: this.level, streak: this.streak, currentStrain: this.currentStrain, timestamp: Date.now() }; localStorage.setItem('growlinger_data', JSON.stringify(data)); } catch(e) { console.warn('Save error (quota?):', e); } },loadStorage() { try { const stored = localStorage.getItem('growlinger_data'); if (stored) { const data = JSON.parse(stored); this.sensorData = data.sensorData || this.sensorData; this.photos = data.photos || []; this.costs = data.costs || []; this.achievements = data.achievements || this.achievements; this.xp = data.xp || 0; this.level = data.level || 1; this.streak = data.streak || 0; this.currentStrain = data.currentStrain; if (data.strains && data.strains.length > 0) { this.strains = [...this.strainDatabase, ...data.strains]; } console.log(`✅ Loaded: ${this.photos.length} photos, ${this.costs.length} costs`); this.renderPhotos(); this.renderCosts(); } } catch(e) { console.warn('Load error:', e); } } };// START APP document.addEventListener('DOMContentLoaded', () => { app.init(); setInterval(() => app.save(), 30000); });
Benachrichtigungen aktivieren OK Sicher nicht?