localgreenchain/lib/environment/analysis.ts
Claude 85591fc348
Add comprehensive environmental tracking for plants
Implements detailed tracking of soil composition, nutrients, climate,
lighting, and surrounding environment to help understand plant success
and optimize growing conditions.

Environmental Data Types (lib/environment/types.ts):
- Soil composition (type, pH, texture, drainage, organic matter)
- Soil amendments (compost, perlite, amendments tracking)
- Nutrient profiles (NPK, secondary nutrients, micronutrients, EC, TDS)
- Fertilizer applications (type, schedule, NPK values)
- Lighting conditions (natural/artificial, hours, spectrum, PPFD/DLI)
- Climate tracking (temperature, humidity, airflow, CO2, USDA zones)
- Growing location (indoor/outdoor/greenhouse with details)
- Container information (type, material, size, drainage)
- Watering schedule (method, frequency, water quality/pH)
- Surrounding environment (companion plants, pests, diseases, ecosystem)
- Growth metrics (measurements, health scores, vigor tracking)

Environmental Analysis (lib/environment/analysis.ts):
- Compare environments with similarity scoring (0-100)
- Generate personalized growing recommendations
- Calculate environmental health scores
- Find plants with similar conditions
- Analyze growth correlations across network
- Identify optimal growing conditions

API Endpoints:
- /api/environment/recommendations - Get plant-specific advice
- /api/environment/similar - Find plants with similar conditions
- /api/environment/compare - Compare two plants environments
- /api/environment/analysis - Network-wide growth correlation

Features:
- Comprehensive soil tracking (pH, texture, drainage, amendments)
- Full NPK and micronutrient monitoring
- Sunlight exposure and artificial light tracking
- Temperature and humidity ranges
- Water quality monitoring (pH, TDS, chlorine)
- Pest and disease tracking
- Companion planting recommendations
- Environmental health scoring with priority-based recommendations
- Growth success analysis across similar environments

Integration:
- Updated PlantData type to include environment and growthMetrics
- Compatible with existing blockchain structure
- Optional environmental data (backward compatible)

Use Cases:
- Track what works for your plants
- Learn from successful growers with similar conditions
- Get personalized recommendations based on your setup
- Compare your environment with thriving plants
- Optimize conditions for better yield/health
- Share growing knowledge with the community
- Research optimal conditions by species

This enables growers to:
- Understand why plants thrive or struggle
- Replicate successful growing conditions
- Make data-driven decisions
- Learn from the collective experience
- Improve propagation success rates
2025-11-16 16:24:38 +00:00

432 lines
14 KiB
TypeScript

/**
* 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<GrowingEnvironment>;
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<string, number>);
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);
}