Files
Smartwave/cloud/microwaveCookPlanner.py
Ninluc 0f17e9dce6
Build, push image, and notify Watchtower / build-image (push) Successful in 3m19s
Build, push image, and notify Watchtower / notify (push) Successful in 7s
Better cook parameter estimation + Defrost mode + Removed esp-wifi debugs + Orchestrator and microwave exchange
2026-07-27 17:14:05 +02:00

100 lines
3.7 KiB
Python

from typing import Dict, Any
from APIs.edamam import EdamamAPI
from microwaveDishAnalyzer import MicrowaveDishAnalyzer
from microwaveThermalEngine import MicrowaveThermalEngine, DishThermalState
class MicrowaveCookPlanner:
"""Orchestrates Edamam API, Dish Analyzer, and Thermal Engine into a single workflow."""
def __init__(self, cm_per_pixel: float = 0.05):
self.edamam_api = EdamamAPI()
self.analyzer = MicrowaveDishAnalyzer(cm_per_pixel=cm_per_pixel)
self.engine = MicrowaveThermalEngine()
def _extract_edamam_data(self, edamam_resp: Dict[str, Any]) -> tuple[str, float, Dict[str, float]]:
"""Parses Edamam Vision response to extract label, total mass, and macronutrient grams."""
recipe = edamam_resp.get("combined", {}).get("recipe", {})
# Fallback to first dish if 'combined' is empty
if not recipe and edamam_resp.get("dishes"):
recipe = edamam_resp["dishes"][0].get("recipe", {})
label = recipe.get("label", "Unknown Dish")
total_weight = float(recipe.get("totalWeight", 300.0)) # Default 300g fallback
nutrients = recipe.get("totalNutrients", {})
# Extract macronutrients in grams (Edamam nutrient codes)
fat_g = float(nutrients.get("FAT", {}).get("quantity", 0.0))
protein_g = float(nutrients.get("PROCNT", {}).get("quantity", 0.0))
carbs_g = float(nutrients.get("CHOCDF", {}).get("quantity", 0.0))
# Water is sometimes omitted in Edamam; infer remaining mass as water if missing
if "WATER" in nutrients:
water_g = float(nutrients["WATER"].get("quantity", 0.0))
else:
water_g = max(0.0, total_weight - (fat_g + protein_g + carbs_g))
macros = {
"water_g": water_g,
"fat_g": fat_g,
"protein_g": protein_g,
"carbs_g": carbs_g,
}
return label, total_weight, macros
def generate_plan(
self,
image_path: str,
height_cm: float,
initial_temp_c: float,
microwave_wattage: int = 900,
defrost_mode: bool = False
) -> Dict[str, Any]:
"""Main pipeline call to parse an image and return cooking parameters."""
# 1. Vision & Nutrient Analysis
edamam_resp = self.edamam_api.analyze_dish_image(image_path)
food_label, edamam_mass_g, macros = self._extract_edamam_data(edamam_resp)
# 2. Geometric Volume Calculation
vol_data = self.analyzer.estimate_volume(
image_path=image_path,
height_cm=height_cm,
food_label=food_label
)
# 3. Mass Cross-Validation & Density Check
mass_data = self.analyzer.reconcile_mass(
edamam_mass_g=edamam_mass_g,
volume_cm3=vol_data["volume_cm3"],
food_label=food_label
)
final_mass_g = mass_data["final_mass_g"]
# 4. Thermal State Creation
thermal_state = DishThermalState(
food_name=food_label,
macronutrients=macros,
estimated_mass_g=final_mass_g,
initial_temp_c=initial_temp_c,
volume_cm3=vol_data["volume_cm3"]
)
# 5. Cook Plan Calculation
cook_plan = self.engine.calculate_cook_plan(
state=thermal_state,
microwave_wattage=microwave_wattage,
defrost_mode=defrost_mode
)
# Return consolidated output
return {
"dish_name": food_label,
"reconciled_mass_g": final_mass_g,
"mass_validation_status": mass_data["status"],
"cook_plan": cook_plan,
"geometry": vol_data
}