Skip to content

Commit b07475d

Browse files
Merge pull request #26 from TheRealFalseReality/copilot/add-camera-visual-analysis-sensor
Add dedicated sensor for camera visual analysis when camera is configured
2 parents 649ccbf + 5619fb4 commit b07475d

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

custom_components/aquarium_ai/__init__.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ def _build_notification_message(notification_format, sensor_data, sensor_mapping
309309
if "water_change_recommendation" in ai_data:
310310
message_parts.append(f"\n💧 Water Change:\n{ai_data['water_change_recommendation']}")
311311

312+
# Add camera visual analysis if available
313+
if "camera_visual_notification_analysis" in ai_data:
314+
message_parts.append(f"\n📷 Camera Analysis:\n{ai_data['camera_visual_notification_analysis']}")
315+
312316
# Add overall analysis
313317
if "overall_notification_analysis" in ai_data:
314318
message_parts.append(f"\n🎯 Overall Assessment:\n{ai_data['overall_notification_analysis']}")
@@ -348,6 +352,10 @@ def _build_notification_message(notification_format, sensor_data, sensor_mapping
348352
if "water_change_recommendation" in ai_data:
349353
message_parts.append(f"\n💧 Water Change: {ai_data['water_change_recommendation']}")
350354

355+
# Add camera visual analysis if available (brief version for condensed)
356+
if "camera_visual_analysis" in ai_data:
357+
message_parts.append(f"\n📷 Camera Analysis: {ai_data['camera_visual_analysis']}")
358+
351359
# Add overall brief analysis (same as used for sensors)
352360
if "overall_analysis" in ai_data:
353361
message_parts.append(f"\n🎯 Overall Assessment: {ai_data['overall_analysis']}")
@@ -387,6 +395,10 @@ def _build_notification_message(notification_format, sensor_data, sensor_mapping
387395
if "water_change_recommendation" in ai_data:
388396
message_parts.append(f"\n💧 Water Change:\n{ai_data['water_change_recommendation']}")
389397

398+
# Add camera visual analysis if available (detailed version)
399+
if "camera_visual_notification_analysis" in ai_data:
400+
message_parts.append(f"\n📷 Camera Analysis:\n{ai_data['camera_visual_notification_analysis']}")
401+
390402
# Add overall detailed analysis
391403
if "overall_notification_analysis" in ai_data:
392404
message_parts.append(f"\n🎯 Overall Assessment:\n{ai_data['overall_notification_analysis']}")
@@ -521,6 +533,18 @@ async def send_ai_aquarium_analysis(now):
521533
# Prepare camera instructions if camera is configured
522534
camera_instructions = ""
523535
if camera:
536+
# Add camera analysis fields to the structure
537+
combined_analysis_structure["camera_visual_analysis"] = {
538+
"description": "Brief 1-2 sentence visual analysis of the aquarium from the camera image (under 200 characters). Focus on water clarity, fish/plant health, and any maintenance needs visible.",
539+
"required": True,
540+
"selector": {"text": None}
541+
}
542+
combined_analysis_structure["camera_visual_notification_analysis"] = {
543+
"description": "Detailed visual analysis of the aquarium from the camera image. Include observations about water clarity, fish identification and behavior, plant health, equipment condition, and any visible maintenance needs. Provide specific observations and recommendations based on what is visible in the image.",
544+
"required": True,
545+
"selector": {"text": None}
546+
}
547+
524548
camera_instructions = """
525549
526550
If an aquarium camera image is provided:
@@ -533,7 +557,10 @@ async def send_ai_aquarium_analysis(now):
533557
* Any visible algae, debris, or maintenance needs
534558
- Focus only on aquarium-related observations that can be determined visually
535559
- Do not attempt to provide numerical measurements from the image
536-
- Integrate visual observations with sensor data when drawing conclusions"""
560+
- Integrate visual observations with sensor data when drawing conclusions
561+
562+
For camera_visual_analysis: Provide a brief 1-2 sentence summary of visual observations (under 200 characters).
563+
For camera_visual_notification_analysis: Provide detailed visual analysis with specific observations and recommendations."""
537564

538565
# Prepare AI Task data with separate instructions for sensor vs notification analysis
539566
ai_task_data = {
@@ -658,6 +685,13 @@ async def send_ai_aquarium_analysis(now):
658685
water_change_rec = water_change_rec[:252] + "..."
659686
sensor_analysis_data["water_change_recommended"] = water_change_rec
660687

688+
# Store camera visual analysis with brief version for sensors (if camera configured)
689+
if "camera_visual_analysis" in ai_data:
690+
camera_analysis = ai_data["camera_visual_analysis"]
691+
if len(camera_analysis) > 255:
692+
camera_analysis = camera_analysis[:252] + "..."
693+
sensor_analysis_data["camera_visual_analysis"] = camera_analysis
694+
661695
# Store the analysis data and sensor data in hass.data for sensors to access
662696
hass.data[DOMAIN][entry.entry_id]["sensor_analysis"] = sensor_analysis_data
663697
hass.data[DOMAIN][entry.entry_id]["sensor_data"] = sensor_data

custom_components/aquarium_ai/sensor.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
CONF_DISSOLVED_OXYGEN_SENSOR,
2020
CONF_WATER_LEVEL_SENSOR,
2121
CONF_ORP_SENSOR,
22+
CONF_CAMERA,
2223
CONF_UPDATE_FREQUENCY,
2324
CONF_AI_TASK,
2425
UPDATE_FREQUENCIES,
@@ -136,6 +137,22 @@ async def async_setup_entry(
136137
)
137138
)
138139

140+
# Create camera visual analysis sensor (only if camera is configured)
141+
camera = config_entry.data.get(CONF_CAMERA)
142+
if camera:
143+
entities.append(
144+
AquariumAICameraAnalysis(
145+
hass,
146+
config_entry,
147+
tank_name,
148+
aquarium_type,
149+
camera,
150+
ai_task,
151+
frequency_minutes,
152+
valid_sensor_mappings,
153+
)
154+
)
155+
139156
async_add_entities(entities)
140157

141158

@@ -696,4 +713,66 @@ async def async_update(self) -> None:
696713
_LOGGER.error("Error updating water change recommendation sensor: %s", err)
697714
self._state = "Analysis unavailable"
698715
self._available = False
716+
self._attr_extra_state_attributes = {}
717+
718+
719+
class AquariumAICameraAnalysis(AquariumAIBaseSensor):
720+
"""Sensor for camera visual analysis."""
721+
722+
def __init__(
723+
self,
724+
hass: HomeAssistant,
725+
config_entry: ConfigEntry,
726+
tank_name: str,
727+
aquarium_type: str,
728+
camera: str,
729+
ai_task: str,
730+
frequency_minutes: Optional[int],
731+
sensor_mappings: list,
732+
):
733+
"""Initialize the camera analysis sensor."""
734+
super().__init__(hass, config_entry, tank_name, aquarium_type, frequency_minutes, sensor_mappings)
735+
self._camera = camera
736+
self._ai_task = ai_task
737+
self._attr_name = f"{tank_name} Camera Analysis"
738+
self._attr_unique_id = f"{config_entry.entry_id}_camera_analysis"
739+
self._attr_icon = "mdi:camera"
740+
self._attr_extra_state_attributes = {}
741+
742+
@property
743+
def extra_state_attributes(self):
744+
"""Return the state attributes."""
745+
return self._attr_extra_state_attributes
746+
747+
async def async_update(self) -> None:
748+
"""Update the sensor."""
749+
try:
750+
# Get shared analysis data
751+
shared_data = self._get_shared_data()
752+
sensor_analysis = shared_data["sensor_analysis"]
753+
754+
if "camera_visual_analysis" in sensor_analysis and sensor_analysis["camera_visual_analysis"]:
755+
# Use the brief camera analysis from the shared update
756+
self._state = sensor_analysis["camera_visual_analysis"]
757+
self._available = True
758+
759+
# Add attributes
760+
self._attr_extra_state_attributes = {
761+
"camera_entity": self._camera,
762+
"aquarium_type": self._aquarium_type,
763+
"last_updated": shared_data.get("last_update"),
764+
"ai_task": self._ai_task,
765+
}
766+
else:
767+
# No analysis available yet
768+
self._state = "No camera analysis available"
769+
self._available = True
770+
self._attr_extra_state_attributes = {
771+
"camera_entity": self._camera,
772+
}
773+
774+
except Exception as err:
775+
_LOGGER.error("Error updating camera analysis sensor: %s", err)
776+
self._state = "Analysis unavailable"
777+
self._available = False
699778
self._attr_extra_state_attributes = {}

0 commit comments

Comments
 (0)