From 828aeb5b88e937872c9fabb04bb8632810017842 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Oct 2025 17:10:08 +0000 Subject: [PATCH 1/4] Add multi-camera support for same source camera MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow users to add the same source camera multiple times with different processing settings. When adding a duplicate source camera, automatically append a suffix (_2, _3, etc.) to the entity name to maintain unique identifiers. Changes: - Remove frontend filter that prevented duplicate camera selection - Remove API validation that blocked duplicate source cameras - Add automatic entity name suffix generation for duplicates - Set CONF_ENTITY_NAME in camera config to preserve naming 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../camera_snapshot_processor/api.py | 30 +++++++++++++++---- .../frontend/panel.js | 5 ++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/custom_components/camera_snapshot_processor/api.py b/custom_components/camera_snapshot_processor/api.py index 1db586b..7fbead0 100644 --- a/custom_components/camera_snapshot_processor/api.py +++ b/custom_components/camera_snapshot_processor/api.py @@ -11,6 +11,7 @@ from homeassistant.core import HomeAssistant from .const import ( + CONF_ENTITY_NAME, CONF_HEIGHT, CONF_KEEP_RATIO, CONF_QUALITY, @@ -97,13 +98,8 @@ async def post(self, request: web.Request) -> web.Response: {"error": "source_camera is required"}, status=400 ) - # Check if camera already exists + # Get existing cameras (allow duplicates for multi-processing same source) cameras = dict(entry.data.get("cameras", {})) - for cam_id, cam_config in cameras.items(): - if cam_config.get(CONF_SOURCE_CAMERA) == source_camera: - return web.json_response( - {"error": "Camera already configured"}, status=400 - ) # Get camera entity to fetch source dimensions camera_entity = self._get_camera_entity(source_camera) @@ -149,6 +145,27 @@ async def post(self, request: web.Request) -> web.Response: default_width = DEFAULT_WIDTH default_height = DEFAULT_HEIGHT + # Generate unique entity name if same source camera is used multiple times + source_name = source_camera.replace("camera.", "") + base_entity_name = f"{source_name}_processed" + + # Count existing cameras with the same source + same_source_count = sum( + 1 for cam_config in cameras.values() + if cam_config.get(CONF_SOURCE_CAMERA) == source_camera + ) + + # If this is a duplicate source, add suffix (_2, _3, etc.) + if same_source_count > 0: + entity_name = f"{base_entity_name}_{same_source_count + 1}" + _LOGGER.info( + "Multiple cameras from same source %s detected. Using entity name: %s", + source_camera, + entity_name, + ) + else: + entity_name = base_entity_name + # Create new camera with smart default config camera_id = str(uuid.uuid4()) cameras[camera_id] = { @@ -160,6 +177,7 @@ async def post(self, request: web.Request) -> web.Response: CONF_KEEP_RATIO: DEFAULT_KEEP_RATIO, CONF_QUALITY: DEFAULT_QUALITY, CONF_STATE_ICONS: [], + CONF_ENTITY_NAME: entity_name, # Set custom entity name for duplicates } # Update entry diff --git a/custom_components/camera_snapshot_processor/frontend/panel.js b/custom_components/camera_snapshot_processor/frontend/panel.js index 76bb0da..867a7f4 100644 --- a/custom_components/camera_snapshot_processor/frontend/panel.js +++ b/custom_components/camera_snapshot_processor/frontend/panel.js @@ -714,9 +714,8 @@ availableCameras = data.cameras || []; - // Filter out already configured cameras - const configuredCameras = Object.values(cameras).map(c => c.source_camera); - availableCameras = availableCameras.filter(c => !configuredCameras.includes(c.entity_id)); + // Note: We allow selecting the same camera multiple times + // to support multiple processed versions of the same source // Populate select const select = document.getElementById('new-camera-select'); From 38543a21e53c322d2c9a725b8f2d32dbd3b6716f Mon Sep 17 00:00:00 2001 From: Patryk Dadas Date: Tue, 21 Oct 2025 23:49:47 +0200 Subject: [PATCH 2/4] Potential fix for code scanning alert no. 34: Log Injection Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- custom_components/camera_snapshot_processor/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/custom_components/camera_snapshot_processor/api.py b/custom_components/camera_snapshot_processor/api.py index 7fbead0..8a6a97d 100644 --- a/custom_components/camera_snapshot_processor/api.py +++ b/custom_components/camera_snapshot_processor/api.py @@ -92,6 +92,9 @@ async def post(self, request: web.Request) -> web.Response: data = await request.json() source_camera = data.get("source_camera") + if source_camera is not None: + # Remove newlines and carriage returns to prevent log injection + source_camera = source_camera.replace('\r', '').replace('\n', '') if not source_camera: return web.json_response( From b9d3c9346d8471659870640df52217ce8429a6ca Mon Sep 17 00:00:00 2001 From: Patryk Dadas Date: Tue, 21 Oct 2025 23:51:22 +0200 Subject: [PATCH 3/4] Potential fix for code scanning alert no. 35: Log Injection Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- custom_components/camera_snapshot_processor/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/camera_snapshot_processor/api.py b/custom_components/camera_snapshot_processor/api.py index 8a6a97d..72dd7ed 100644 --- a/custom_components/camera_snapshot_processor/api.py +++ b/custom_components/camera_snapshot_processor/api.py @@ -164,7 +164,7 @@ async def post(self, request: web.Request) -> web.Response: _LOGGER.info( "Multiple cameras from same source %s detected. Using entity name: %s", source_camera, - entity_name, + entity_name.replace('\r', '').replace('\n', ''), ) else: entity_name = base_entity_name From 3b67d2888dd130b57cdbae9ad27a45398b0a8757 Mon Sep 17 00:00:00 2001 From: Patryk Dadas Date: Tue, 21 Oct 2025 23:54:46 +0200 Subject: [PATCH 4/4] Potential fix for code scanning alert no. 36: Log Injection Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- custom_components/camera_snapshot_processor/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/camera_snapshot_processor/api.py b/custom_components/camera_snapshot_processor/api.py index 72dd7ed..c95f09f 100644 --- a/custom_components/camera_snapshot_processor/api.py +++ b/custom_components/camera_snapshot_processor/api.py @@ -163,7 +163,7 @@ async def post(self, request: web.Request) -> web.Response: entity_name = f"{base_entity_name}_{same_source_count + 1}" _LOGGER.info( "Multiple cameras from same source %s detected. Using entity name: %s", - source_camera, + source_camera.replace('\r', '').replace('\n', ''), entity_name.replace('\r', '').replace('\n', ''), ) else: