Compare commits
2 Commits
755ffdad46
...
3711928c7d
| Author | SHA1 | Date | |
|---|---|---|---|
| 3711928c7d | |||
| 6b8c1688a6 |
+24
-3
@@ -17,6 +17,7 @@ from APIs.mqtt import send_command
|
|||||||
from APIs.webex import WebexManager
|
from APIs.webex import WebexManager
|
||||||
from APIs.shodan import ShodanAuditor
|
from APIs.shodan import ShodanAuditor
|
||||||
from APIs.twilio import send_alert_sms
|
from APIs.twilio import send_alert_sms
|
||||||
|
from cloud.image_enhancer import enhance_image_for_ai
|
||||||
from microwaveCookPlanner import MicrowaveCookPlanner
|
from microwaveCookPlanner import MicrowaveCookPlanner
|
||||||
import safety_checker
|
import safety_checker
|
||||||
from jobs import job_server_cve_audit, job_client_ip_audit, job_request_telemetry
|
from jobs import job_server_cve_audit, job_client_ip_audit, job_request_telemetry
|
||||||
@@ -153,7 +154,7 @@ start_scheduler_once()
|
|||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Authentication Middleware
|
# Authentication Middleware
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
EXEMPT_ROUTES = {'hello_world', 'oauth_callback', 'odata_metadata', 'debug_telemetryrequest', 'trigger_job_manually'}
|
EXEMPT_ROUTES = {'hello_world', 'oauth_callback', 'odata_metadata', 'debug_telemetryrequest', 'trigger_job_manually', 'debug_unsafe_dish', 'debug_safe_dish'}
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def authenticate_request():
|
def authenticate_request():
|
||||||
@@ -245,12 +246,14 @@ async def cooking_params():
|
|||||||
with open(filepath, "wb") as f:
|
with open(filepath, "wb") as f:
|
||||||
f.write(base64.b64decode(camera_image_b64))
|
f.write(base64.b64decode(camera_image_b64))
|
||||||
data["camera_image"] = filepath
|
data["camera_image"] = filepath
|
||||||
|
|
||||||
|
enhanced_filepath = enhance_image_for_ai(filepath)
|
||||||
|
|
||||||
# Concurrent AI Execution
|
# Concurrent AI Execution
|
||||||
safety_task = asyncio.to_thread(safety_checker.check_dish_safety, filepath)
|
safety_task = asyncio.to_thread(safety_checker.check_dish_safety, enhanced_filepath)
|
||||||
planner_task = asyncio.to_thread(
|
planner_task = asyncio.to_thread(
|
||||||
microwave_cook_planner.generate_plan,
|
microwave_cook_planner.generate_plan,
|
||||||
image_path=filepath,
|
image_path=enhanced_filepath,
|
||||||
height_cm=height_cm,
|
height_cm=height_cm,
|
||||||
initial_temp_c=initial_temp_c,
|
initial_temp_c=initial_temp_c,
|
||||||
microwave_wattage=microwave_wattage,
|
microwave_wattage=microwave_wattage,
|
||||||
@@ -584,6 +587,24 @@ def trigger_job_manually(job_id):
|
|||||||
"status": "error",
|
"status": "error",
|
||||||
"message": f"Job execution failed: {str(e)}"
|
"message": f"Job execution failed: {str(e)}"
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
@app.route("/debug/dishsafety/not_safe", methods=["GET"])
|
||||||
|
def debug_unsafe_dish():
|
||||||
|
"""Debug endpoint to simulate an unsafe dish scenario."""
|
||||||
|
# Simulated unsafe dish data
|
||||||
|
unsafe_dish = "storage/dishPhotos/dish_78827473286c4f67855f3fbd0ccb9bb4.jpg"
|
||||||
|
|
||||||
|
# Call the cooking_params endpoint logic directly
|
||||||
|
return safety_checker.check_dish_safety(unsafe_dish)
|
||||||
|
|
||||||
|
@app.route("/debug/dishsafety/safe", methods=["GET"])
|
||||||
|
def debug_safe_dish():
|
||||||
|
"""Debug endpoint to simulate a safe dish scenario."""
|
||||||
|
# Simulated safe dish data
|
||||||
|
unsafe_dish = "storage/dishPhotos/dish_972251744d034b32bb5134ff9d0f071c.jpg"
|
||||||
|
|
||||||
|
# Call the cooking_params endpoint logic directly
|
||||||
|
return safety_checker.check_dish_safety(unsafe_dish)
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# OData Metadata Definition
|
# OData Metadata Definition
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import cv2
|
||||||
|
|
||||||
|
def enhance_image_for_ai(input_path: str) -> str:
|
||||||
|
"""Enhances dark areas and contrast using CLAHE without blowing out bright areas."""
|
||||||
|
img = cv2.imread(input_path)
|
||||||
|
if img is None:
|
||||||
|
return input_path
|
||||||
|
|
||||||
|
# Convert to LAB color space to modify luminance channel only
|
||||||
|
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
||||||
|
l, a, b = cv2.split(lab)
|
||||||
|
|
||||||
|
# Apply CLAHE to Lightness channel
|
||||||
|
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
||||||
|
cl = clahe.apply(l)
|
||||||
|
|
||||||
|
# Merge channels and convert back to BGR
|
||||||
|
limg = cv2.merge((cl, a, b))
|
||||||
|
enhanced = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
|
||||||
|
|
||||||
|
enhanced_path = input_path.replace(".jpg", "_enhanced.jpg")
|
||||||
|
cv2.imwrite(enhanced_path, enhanced)
|
||||||
|
return enhanced_path
|
||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from APIs.aichat import generate
|
from APIs.aichat import generate
|
||||||
import shared.config as config
|
import shared.config as config
|
||||||
|
from PIL import Image, ImageEnhance
|
||||||
|
|
||||||
class UtensilMaterialCheck(BaseModel):
|
class UtensilMaterialCheck(BaseModel):
|
||||||
object_name: str = Field(
|
object_name: str = Field(
|
||||||
@@ -28,6 +29,21 @@ class DishSafetyResult(BaseModel):
|
|||||||
description="If is_safe is False, set to 'REMOVE METAL UTENSIL OR FOIL BEFORE MICROWAVING'. Otherwise empty ''."
|
description="If is_safe is False, set to 'REMOVE METAL UTENSIL OR FOIL BEFORE MICROWAVING'. Otherwise empty ''."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def preprocess_dish_image(image_path: str) -> str:
|
||||||
|
"""Brightens dark areas and sharpens food texture for small vision models."""
|
||||||
|
img = Image.open(image_path)
|
||||||
|
|
||||||
|
# 1. Boost brightness slightly
|
||||||
|
enhancer = ImageEnhance.Brightness(img)
|
||||||
|
img = enhancer.enhance(1.4)
|
||||||
|
|
||||||
|
# 2. Boost contrast to separate food shapes from dark shadows
|
||||||
|
enhancer = ImageEnhance.Contrast(img)
|
||||||
|
img = enhancer.enhance(1.3)
|
||||||
|
|
||||||
|
processed_path = "/tmp/processed_dish.jpg"
|
||||||
|
img.save(processed_path, quality=85)
|
||||||
|
return processed_path
|
||||||
|
|
||||||
def check_dish_safety(image_path: str) -> dict:
|
def check_dish_safety(image_path: str) -> dict:
|
||||||
prompt = (
|
prompt = (
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ def on_microwave_change(state: MicrowaveState, field: MicrowaveStateFields):
|
|||||||
if field != MicrowaveStateFields.STATE: # No external screen or lcd uses this field, so we can skip updates for it
|
if field != MicrowaveStateFields.STATE: # No external screen or lcd uses this field, so we can skip updates for it
|
||||||
notify_display_update()
|
notify_display_update()
|
||||||
|
|
||||||
if field == MicrowaveStateFields.STATE and state.state in (MicrowaveState.DONE, MicrowaveState.ANALYZING, MicrowaveState.WAITING_FOR_CLOUD, MicrowaveState.ALERT):
|
if field == MicrowaveStateFields.STATE and state.state in (MicrowaveState.IDLE, MicrowaveState.DONE, MicrowaveState.ANALYZING, MicrowaveState.WAITING_FOR_CLOUD, MicrowaveState.ALERT):
|
||||||
# Send to LoRa device if state changed
|
# Send to LoRa device if state changed
|
||||||
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
|
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
|
||||||
|
|
||||||
@@ -457,11 +457,11 @@ button.start_button_monitoring_thread()
|
|||||||
# --- Alert manager ---
|
# --- Alert manager ---
|
||||||
alert_manager = None
|
alert_manager = None
|
||||||
|
|
||||||
def on_new_alert(alert: Alert):
|
async def on_new_alert(alert: Alert):
|
||||||
# Sends to cloud server
|
# Sends to cloud server
|
||||||
alert_dict = alert.to_dict()
|
alert_dict = alert.to_dict()
|
||||||
|
|
||||||
logs_list = asyncio.run(get_systemd_logs(lines=200, service_name="smartwave"))
|
logs_list = await get_systemd_logs(lines=200, service_name="smartwave")
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"orchestrator_id": DEVICE_ID,
|
"orchestrator_id": DEVICE_ID,
|
||||||
@@ -485,6 +485,7 @@ def on_new_alert(alert: Alert):
|
|||||||
print(f"[Alert Handling] Sending alert to LoRa device: {payload}")
|
print(f"[Alert Handling] Sending alert to LoRa device: {payload}")
|
||||||
if lora.send_reliable(payload):
|
if lora.send_reliable(payload):
|
||||||
microwave_states["2"].set_cooking_state(CookingStates.ALERT)
|
microwave_states["2"].set_cooking_state(CookingStates.ALERT)
|
||||||
|
buzzer.buzzer_siren(beatsNb=5)
|
||||||
|
|
||||||
# --- HARDWARE CONTROLLERS ---
|
# --- HARDWARE CONTROLLERS ---
|
||||||
def _stop_hardware(microwave_id: str):
|
def _stop_hardware(microwave_id: str):
|
||||||
|
|||||||
+16
-2
@@ -1,4 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
|
import asyncio
|
||||||
|
import inspect
|
||||||
|
|
||||||
class AlertManager:
|
class AlertManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -8,10 +10,22 @@ class AlertManager:
|
|||||||
def set_on_alert_callback(self, callback):
|
def set_on_alert_callback(self, callback):
|
||||||
self.on_alert_callback = callback
|
self.on_alert_callback = callback
|
||||||
|
|
||||||
|
def call_on_alert_callback(self, alert):
|
||||||
|
if self.on_alert_callback:
|
||||||
|
if inspect.iscoroutinefunction(self.on_alert_callback):
|
||||||
|
# Schedule coroutine on the running loop without blocking
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
loop.create_task(self.on_alert_callback(alert))
|
||||||
|
except RuntimeError:
|
||||||
|
# Fallback if called outside an active event loop
|
||||||
|
asyncio.run(self.on_alert_callback(alert))
|
||||||
|
else:
|
||||||
|
self.on_alert_callback(alert)
|
||||||
|
|
||||||
def add_alert(self, alert):
|
def add_alert(self, alert):
|
||||||
self.alerts.append(alert)
|
self.alerts.append(alert)
|
||||||
if self.on_alert_callback:
|
self.call_on_alert_callback(alert)
|
||||||
self.on_alert_callback(alert)
|
|
||||||
|
|
||||||
def get_alerts(self):
|
def get_alerts(self):
|
||||||
return self.alerts
|
return self.alerts
|
||||||
|
|||||||
Reference in New Issue
Block a user