/** * Environmental Analysis Service * Compares growing conditions, generates recommendations, and analyzes correlations */ import { GrowingEnvironment, EnvironmentalComparison, EnvironmentalRecommendation, SoilComposition, ClimateConditions, LightingConditions, NutrientProfile, } from './types'; import { PlantData } from '../blockchain/types'; /** * Compare two growing environments and return similarity score */ export function compareEnvironments( env1: GrowingEnvironment, env2: GrowingEnvironment ): EnvironmentalComparison { const similarities: string[] = []; const differences: string[] = []; let score = 0; let maxScore = 0; // Compare location type (10 points) maxScore += 10; if (env1.location.type === env2.location.type) { score += 10; similarities.push(`Both ${env1.location.type} growing`); } else { differences.push(`Location: ${env1.location.type} vs ${env2.location.type}`); } // Compare soil type (15 points) maxScore += 15; if (env1.soil.type === env2.soil.type) { score += 15; similarities.push(`Same soil type: ${env1.soil.type}`); } else { differences.push(`Soil: ${env1.soil.type} vs ${env2.soil.type}`); } // Compare soil pH (10 points, within 0.5 range) maxScore += 10; const pHDiff = Math.abs(env1.soil.pH - env2.soil.pH); if (pHDiff <= 0.5) { score += 10; similarities.push(`Similar soil pH: ${env1.soil.pH.toFixed(1)} ≈ ${env2.soil.pH.toFixed(1)}`); } else if (pHDiff <= 1.0) { score += 5; differences.push(`Soil pH: ${env1.soil.pH.toFixed(1)} vs ${env2.soil.pH.toFixed(1)}`); } else { differences.push(`Soil pH differs significantly: ${env1.soil.pH.toFixed(1)} vs ${env2.soil.pH.toFixed(1)}`); } // Compare temperature (15 points) maxScore += 15; const tempDiff = Math.abs(env1.climate.temperatureDay - env2.climate.temperatureDay); if (tempDiff <= 3) { score += 15; similarities.push(`Similar temperatures: ${env1.climate.temperatureDay}°C ≈ ${env2.climate.temperatureDay}°C`); } else if (tempDiff <= 7) { score += 8; differences.push(`Temperature: ${env1.climate.temperatureDay}°C vs ${env2.climate.temperatureDay}°C`); } else { differences.push(`Temperature differs significantly: ${env1.climate.temperatureDay}°C vs ${env2.climate.temperatureDay}°C`); } // Compare humidity (10 points) maxScore += 10; const humidityDiff = Math.abs(env1.climate.humidityAverage - env2.climate.humidityAverage); if (humidityDiff <= 10) { score += 10; similarities.push(`Similar humidity: ${env1.climate.humidityAverage}% ≈ ${env2.climate.humidityAverage}%`); } else if (humidityDiff <= 20) { score += 5; } else { differences.push(`Humidity: ${env1.climate.humidityAverage}% vs ${env2.climate.humidityAverage}%`); } // Compare lighting type (15 points) maxScore += 15; if (env1.lighting.type === env2.lighting.type) { score += 15; similarities.push(`Same lighting: ${env1.lighting.type}`); } else { differences.push(`Lighting: ${env1.lighting.type} vs ${env2.lighting.type}`); } // Compare natural light exposure if both use natural light (10 points) if (env1.lighting.naturalLight && env2.lighting.naturalLight) { maxScore += 10; if (env1.lighting.naturalLight.exposure === env2.lighting.naturalLight.exposure) { score += 10; similarities.push(`Same sun exposure: ${env1.lighting.naturalLight.exposure}`); } else { differences.push(`Sun exposure: ${env1.lighting.naturalLight.exposure} vs ${env2.lighting.naturalLight.exposure}`); } } // Compare water source (5 points) maxScore += 5; if (env1.watering.waterSource === env2.watering.waterSource) { score += 5; similarities.push(`Same water source: ${env1.watering.waterSource}`); } else { differences.push(`Water: ${env1.watering.waterSource} vs ${env2.watering.waterSource}`); } // Compare container type (10 points) if (env1.container && env2.container) { maxScore += 10; if (env1.container.type === env2.container.type) { score += 10; similarities.push(`Same container: ${env1.container.type}`); } else { differences.push(`Container: ${env1.container.type} vs ${env2.container.type}`); } } // Calculate final score as percentage const finalScore = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0; return { plant1: '', // Will be filled by caller plant2: '', // Will be filled by caller similarities, differences, score: finalScore, }; } /** * Generate environmental recommendations based on plant data */ export function generateRecommendations( plant: PlantData ): EnvironmentalRecommendation[] { const recommendations: EnvironmentalRecommendation[] = []; if (!plant.environment) { recommendations.push({ category: 'general', priority: 'high', issue: 'No environmental data', recommendation: 'Add environmental information to track growing conditions', impact: 'Environmental tracking helps optimize growing conditions and share knowledge', }); return recommendations; } const env = plant.environment; // Soil pH recommendations if (env.soil.pH < 5.5) { recommendations.push({ category: 'soil', priority: 'high', issue: `Low soil pH (${env.soil.pH})`, recommendation: 'Add lime to raise pH. Most plants prefer 6.0-7.0', impact: 'Acidic soil can lock out nutrients and harm roots', }); } else if (env.soil.pH > 7.5) { recommendations.push({ category: 'soil', priority: 'high', issue: `High soil pH (${env.soil.pH})`, recommendation: 'Add sulfur to lower pH. Most plants prefer 6.0-7.0', impact: 'Alkaline soil can prevent nutrient uptake', }); } // Organic matter recommendations if (env.soil.organicMatter < 3) { recommendations.push({ category: 'soil', priority: 'medium', issue: `Low organic matter (${env.soil.organicMatter}%)`, recommendation: 'Add compost or aged manure to improve soil structure', impact: 'Increases water retention and nutrient availability', }); } // Drainage recommendations if (env.soil.drainage === 'poor') { recommendations.push({ category: 'soil', priority: 'high', issue: 'Poor soil drainage', recommendation: 'Add perlite, sand, or create raised beds', impact: 'Prevents root rot and improves oxygen availability', }); } // Temperature recommendations if (env.climate.temperatureDay > 35) { recommendations.push({ category: 'climate', priority: 'high', issue: `High daytime temperature (${env.climate.temperatureDay}°C)`, recommendation: 'Provide shade during hottest hours, increase watering', impact: 'Extreme heat can cause stress and reduce growth', }); } else if (env.climate.temperatureDay < 10) { recommendations.push({ category: 'climate', priority: 'high', issue: `Low daytime temperature (${env.climate.temperatureDay}°C)`, recommendation: 'Provide frost protection or move indoors', impact: 'Cold temperatures can slow growth or damage plants', }); } // Humidity recommendations if (env.climate.humidityAverage < 30) { recommendations.push({ category: 'climate', priority: 'medium', issue: `Low humidity (${env.climate.humidityAverage}%)`, recommendation: 'Mist plants, use humidity tray, or group plants together', impact: 'Low humidity can cause leaf tip browning and stress', }); } else if (env.climate.humidityAverage > 80) { recommendations.push({ category: 'climate', priority: 'medium', issue: `High humidity (${env.climate.humidityAverage}%)`, recommendation: 'Improve air circulation to prevent fungal issues', impact: 'High humidity can promote mold and disease', }); } // Lighting recommendations if (env.lighting.type === 'natural' && env.lighting.naturalLight) { const hoursPerDay = env.lighting.naturalLight.hoursPerDay; if (hoursPerDay < 4 && plant.status === 'growing') { recommendations.push({ category: 'light', priority: 'high', issue: `Insufficient light (${hoursPerDay} hours/day)`, recommendation: 'Move to brighter location or supplement with grow lights', impact: 'Inadequate light causes leggy growth and poor health', }); } } // Nutrient recommendations if (env.nutrients) { const npk = [env.nutrients.nitrogen, env.nutrients.phosphorus, env.nutrients.potassium]; if (npk.some(n => n < 1)) { recommendations.push({ category: 'nutrients', priority: 'medium', issue: `Low NPK levels (${npk.join('-')})`, recommendation: 'Apply balanced fertilizer according to plant needs', impact: 'Nutrient deficiency reduces growth and yield', }); } } // Water quality recommendations if (env.watering.waterQuality) { if (env.watering.waterQuality.pH && env.watering.waterQuality.pH > 7.5) { recommendations.push({ category: 'water', priority: 'low', issue: `Alkaline water pH (${env.watering.waterQuality.pH})`, recommendation: 'Consider pH adjustment or rainwater collection', impact: 'High water pH can gradually raise soil pH', }); } if (env.watering.waterQuality.chlorine === 'yes') { recommendations.push({ category: 'water', priority: 'low', issue: 'Chlorinated water', recommendation: 'Let water sit 24h before use or use filter', impact: 'Chlorine can harm beneficial soil microbes', }); } } // Container recommendations if (env.container && env.container.drainage === 'no') { recommendations.push({ category: 'general', priority: 'critical', issue: 'Container has no drainage', recommendation: 'Add drainage holes immediately to prevent root rot', impact: 'Critical: Standing water will kill most plants', }); } return recommendations.sort((a, b) => { const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; }); } /** * Find plants with similar growing conditions */ export function findSimilarEnvironments( targetPlant: PlantData, allPlants: PlantData[], minScore: number = 70 ): Array<{ plant: PlantData; comparison: EnvironmentalComparison }> { if (!targetPlant.environment) { return []; } const similar: Array<{ plant: PlantData; comparison: EnvironmentalComparison }> = []; for (const plant of allPlants) { // Skip self and plants without environment data if (plant.id === targetPlant.id || !plant.environment) { continue; } const comparison = compareEnvironments(targetPlant.environment, plant.environment); comparison.plant1 = targetPlant.id; comparison.plant2 = plant.id; if (comparison.score >= minScore) { similar.push({ plant, comparison }); } } // Sort by similarity score (highest first) return similar.sort((a, b) => b.comparison.score - a.comparison.score); } /** * Analyze growth success based on environmental factors */ export function analyzeGrowthCorrelation(plants: PlantData[]): { bestConditions: Partial; insights: string[]; } { const successfulPlants = plants.filter( p => p.environment && (p.status === 'mature' || p.status === 'flowering' || p.status === 'fruiting') ); if (successfulPlants.length === 0) { return { bestConditions: {}, insights: ['Not enough data to determine optimal conditions'], }; } const insights: string[] = []; // Analyze soil pH const pHValues = successfulPlants .filter(p => p.environment?.soil.pH) .map(p => p.environment!.soil.pH); if (pHValues.length > 0) { const avgPH = pHValues.reduce((a, b) => a + b, 0) / pHValues.length; insights.push(`Successful plants average soil pH: ${avgPH.toFixed(1)}`); } // Analyze temperature const temps = successfulPlants .filter(p => p.environment?.climate.temperatureDay) .map(p => p.environment!.climate.temperatureDay); if (temps.length > 0) { const avgTemp = temps.reduce((a, b) => a + b, 0) / temps.length; const minTemp = Math.min(...temps); const maxTemp = Math.max(...temps); insights.push(`Successful temperature range: ${minTemp}-${maxTemp}°C (avg: ${avgTemp.toFixed(1)}°C)`); } // Analyze lighting const lightingTypes = successfulPlants .filter(p => p.environment?.lighting.type) .map(p => p.environment!.lighting.type); const lightCount = lightingTypes.reduce((acc, type) => { acc[type] = (acc[type] || 0) + 1; return acc; }, {} as Record); const mostCommonLight = Object.entries(lightCount) .sort((a, b) => b[1] - a[1])[0]; if (mostCommonLight) { insights.push(`Most successful lighting: ${mostCommonLight[0]} (${mostCommonLight[1]} plants)`); } return { bestConditions: { // Would be filled with actual aggregate data }, insights, }; } /** * Calculate environmental health score (0-100) */ export function calculateEnvironmentalHealth(env: GrowingEnvironment): number { let score = 100; let deductions = 0; // Soil health (up to -20 points) if (env.soil.pH < 5.5 || env.soil.pH > 7.5) deductions += 10; if (env.soil.drainage === 'poor') deductions += 10; if (env.soil.organicMatter < 3) deductions += 5; // Climate (up to -20 points) if (env.climate.temperatureDay > 35 || env.climate.temperatureDay < 10) deductions += 10; if (env.climate.humidityAverage < 30 || env.climate.humidityAverage > 80) deductions += 5; if (env.climate.airflow === 'none') deductions += 5; // Lighting (up to -15 points) if (env.lighting.naturalLight && env.lighting.naturalLight.hoursPerDay < 4) deductions += 15; // Container (up to -15 points) if (env.container && env.container.drainage === 'no') deductions += 15; // Water (up to -10 points) if (env.watering.waterQuality?.chlorine === 'yes') deductions += 5; if (env.watering.waterQuality?.pH && env.watering.waterQuality.pH > 7.5) deductions += 5; return Math.max(0, score - deductions); }