update-tool
Update Rhombus device configurations like camera video, audio, and device settings through a guided multi-step process.
Instructions
This tool allows updating configuration settings for various Rhombus entities. Currently supports:
Cameras: Update video settings (resolution, HDR, WDR, brightness, contrast, etc.), audio settings (recording, microphone, speaker), and device settings (name, timezone, LED control).
For LED control, use EXACTLY these field names in cameraDeviceSettings:
To turn LED off: {"led_stealth_mode": true} (recommended) or {"led_mode": "always_off"}
To turn LED on: {"led_stealth_mode": false} or {"led_mode": "always_on"} or {"led_mode": "auto"}
IMPORTANT: Use underscore in field names (led_mode, led_stealth_mode), not camelCase
The tool supports faceted UUIDs (e.g., "cameraUuid.v0" or "cameraUuid.v1") to update specific camera facets. If no facet is specified, defaults to "v0".
The tool guides users through a multi-step process:
Entity selection (if not provided)
Settings configuration with current values shown
Confirmation and application of changes
Future support planned for:
Climate sensors
Door controllers
Environmental gateways
Audio gateways
Doorbell cameras
Badge readers
The tool uses elicitation forms for rich user interaction and shows current settings before updates.
Output filtering (all tools):
includeFields(string[]): Dot-notation paths to keep in the response (e.g."vehicleEvents.vehicleLicensePlate"). Omit to return all fields.filterBy(array): Predicates to filter array items. Each entry:{field, op, value}where op is one of= != > >= < <= contains. All conditions are ANDed. Example:[{field:"vehicleLicensePlate", op:"=", value:"ABC123"}]WARNING: some tool responses exceed 400k characters — use these params to request only the data you need.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| entityType | Yes | Type of entity to update | |
| entityUuid | Yes | UUID of the entity to update | |
| cameraVideoSettings | Yes | JSON string of video settings to update for camera | |
| cameraAudioSettings | Yes | JSON string of audio settings to update for camera | |
| cameraDeviceSettings | Yes | JSON string of device settings to update for camera | |
| step | Yes | Current step in the update process | |
| includeFields | Yes | Dot-notation field paths to include in the response (e.g. "vehicleEvents.vehicleLicensePlate"). Pass null to return all fields. WARNING: some responses can exceed 400k characters — use includeFields to request only the data you need. For high-volume tools this may be required to get a complete answer. | |
| filterBy | Yes | Filter array items in the response by field values. All conditions are ANDed. Example: [{field: "vehicleLicensePlate", op: "=", value: "ABC123"}, {field: "confidence", op: ">", value: 0.8}] Use alongside includeFields to get only the specific records and fields you need. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| needUserInput | No | ||
| message | No | ||
| requestType | No | ||
| submitAction | No | ||
| entityType | No | ||
| entityUuid | No | ||
| currentSettings | No | ||
| success | No | ||
| error | No | ||
| updatedSettings | No |
Implementation Reference
- src/tools/update-tool.ts:51-384 (handler)Main handler function that executes the update-tool logic. Implements a multi-step process: entity type selection, entity UUID selection, settings configuration, validation, and applying updates via the API. Currently supports camera settings updates (video, audio, device).
const TOOL_HANDLER = async (args: ToolArgs, extra: any) => { const { entityType, entityUuid, cameraVideoSettings, cameraAudioSettings, cameraDeviceSettings, step, } = args; // Handle camera updates if (entityType === "camera") { // Step 3: Apply settings (if settings provided) if ( entityUuid && ((cameraVideoSettings && cameraVideoSettings.trim()) || (cameraAudioSettings && cameraAudioSettings.trim()) || (cameraDeviceSettings && cameraDeviceSettings.trim())) ) { try { // Parse the faceted UUID to extract base UUID and facet const { baseUuid, facet } = parseFacetedUuid(entityUuid); const updatePayload: any = { configUpdate: { deviceUuid: baseUuid, }, }; // Parse and add video settings if (cameraVideoSettings && cameraVideoSettings.trim()) { const videoSettings = JSON.parse(cameraVideoSettings); // Validate with zod schema const validatedVideo = CameraVideoSettings.parse(videoSettings); updatePayload.configUpdate.videoFacetSettings = { [facet]: cleanUpdatePayload(validatedVideo), }; } // Parse and add audio settings if (cameraAudioSettings && cameraAudioSettings.trim()) { const audioSettings = JSON.parse(cameraAudioSettings); // Validate with zod schema const validatedAudio = CameraAudioSettings.parse(audioSettings); updatePayload.configUpdate.audioFacetSettings = { [facet]: cleanUpdatePayload(validatedAudio), }; } // Parse and add device settings if (cameraDeviceSettings && cameraDeviceSettings.trim()) { const deviceSettings = JSON.parse(cameraDeviceSettings); logger.debug("[update-tool] Raw device settings:", deviceSettings); // Transform ledMode to led_mode and OFF to always_off before validation if ("ledMode" in deviceSettings) { deviceSettings.led_mode = deviceSettings.ledMode === "OFF" ? "always_off" : deviceSettings.ledMode; delete deviceSettings.ledMode; } // Convert string "true"/"false" to boolean for led_stealth_mode if ( "led_stealth_mode" in deviceSettings && typeof deviceSettings.led_stealth_mode === "string" ) { deviceSettings.led_stealth_mode = deviceSettings.led_stealth_mode === "true"; } // Validate with zod schema const validatedDevice = CameraDeviceSettings.parse(deviceSettings); logger.debug("[update-tool] Validated device settings:", validatedDevice); const cleaned = cleanUpdatePayload(validatedDevice); logger.debug("[update-tool] Cleaned device settings:", cleaned); updatePayload.configUpdate.deviceSettings = cleaned; } // Validate the full payload const validatedPayload = UpdateCameraConfigPayload.parse(updatePayload); const featureValidation = await validateCameraFeatureSupport( validatedPayload, extra._meta?.requestModifiers as RequestModifiers, extra.sessionId ); if (!featureValidation.canProceed) { return { content: [ { type: "text" as const, text: featureValidation.error || "This camera does not support one or more requested features.", }, ], }; } // Apply the updates const result = await updateCameraConfig( validatedPayload, extra._meta?.requestModifiers as RequestModifiers, extra.sessionId ); if (!result.success) { const hasCapabilitySensitiveChange = hasCapabilitySensitiveSettings(validatedPayload); const normalizedError = hasCapabilitySensitiveChange ? "This camera may not support one or more requested settings." : result.error; return { content: [ { type: "text" as const, text: `Failed to update camera settings: ${normalizedError}`, }, ], }; } // Format the updated settings for display const formattedSettings = formatCameraSettings(validatedPayload.configUpdate); const jsonResultResponse = { needUserInput: false, success: true, message: `✅ Camera settings updated successfully!\n\n${formattedSettings}`, entityType: "camera", entityUuid, updatedSettings: validatedPayload.configUpdate, }; return { content: [ { type: "text" as const, text: jsonResultResponse.message, }, ], structuredContent: jsonResultResponse, }; } catch (error) { return { content: [ { type: "text" as const, text: `Error updating camera settings: ${ error instanceof Error ? error.message : "Unknown error" }`, }, ], }; } } // Step 2: Show settings configuration form (if entityUuid provided but no settings) if (entityUuid && step !== "entity-selection") { try { // Parse the faceted UUID to extract base UUID const { baseUuid } = parseFacetedUuid(entityUuid); // Get current camera details const cameraDetails = await getCameraDetails( baseUuid, extra._meta?.requestModifiers as RequestModifiers, extra.sessionId ); if (!cameraDetails.success || !cameraDetails.data) { return { content: [ { type: "text" as const, text: `Failed to get camera details: ${cameraDetails.error || "Camera not found"}`, }, ], }; } const camera = cameraDetails.data; const currentVideoSettings = camera.videoSettings || {}; const currentAudioSettings = camera.audioSettings || {}; const currentDeviceSettings = { camera_name: camera.name, camera_timezone: camera.timezone, led_intensity: camera.ledIntensity, led_mode: camera.ledMode, }; const jsonResultResponse = { needUserInput: true, message: `Configure settings for camera "${camera.name}" (${entityUuid})\n\nCurrent settings are shown below. Modify the values you want to change:`, requestType: "camera-settings-configuration", submitAction: "update-tool", entityType: "camera", entityUuid, currentSettings: { video: currentVideoSettings, audio: currentAudioSettings, device: currentDeviceSettings, }, }; return { content: [ { type: "text" as const, text: JSON.stringify(jsonResultResponse), }, ], structuredContent: jsonResultResponse, }; } catch (error) { return { content: [ { type: "text" as const, text: `Error retrieving camera details: ${ error instanceof Error ? error.message : "Unknown error" }`, }, ], }; } } // Step 1: Entity selection (if no entityUuid) if (!entityUuid) { const jsonResultResponse = { needUserInput: true, message: "Please provide the camera UUID you want to update.\n\nYou can provide a faceted UUID (e.g., 'cameraUuid.v0' or 'cameraUuid.v1') to update a specific facet, or just the base UUID to update facet v0 by default.\n\nYou can find the camera UUID in the Rhombus console or by using the search tools.", requestType: "entity-selection", submitAction: "update-tool", entityType: "camera", }; return { content: [ { type: "text" as const, text: JSON.stringify(jsonResultResponse), }, ], structuredContent: jsonResultResponse, }; } } // Handle other entity types (future implementation) if (entityType === "climate-sensor") { return { content: [ { type: "text" as const, text: "Climate sensor updates are not yet implemented. Coming soon!", }, ], }; } if (entityType === "door-controller") { return { content: [ { type: "text" as const, text: "Door controller updates are not yet implemented. Coming soon!", }, ], }; } if (entityType === "environmental-gateway") { return { content: [ { type: "text" as const, text: "Environmental gateway updates are not yet implemented. Coming soon!", }, ], }; } if (entityType === "audio-gateway") { return { content: [ { type: "text" as const, text: "Audio gateway updates are not yet fully implemented. Coming soon!", }, ], }; } if (entityType === "doorbell-camera") { return { content: [ { type: "text" as const, text: "Doorbell camera updates are not yet fully implemented. Coming soon!", }, ], }; } if (entityType === "badge-reader") { return { content: [ { type: "text" as const, text: "Badge reader updates are not yet fully implemented. Coming soon!", }, ], }; } // Step 0: Show initial form (no entityType provided) return { content: [ { type: "text" as const, text: JSON.stringify({ needUserInput: true, message: "Welcome to the Rhombus entity update tool!\n\nWhat type of entity would you like to update?\n\n• **camera** - Update camera video, audio, or device settings\n• **climate-sensor** - Update climate sensor settings (coming soon)\n• **door-controller** - Update door controller settings (coming soon)\n• **environmental-gateway** - Update environmental gateway settings (coming soon)\n\nPlease specify the entity type to continue.", requestType: "entity-type-selection", submitAction: "update-tool", }), }, ], }; }; - src/tools/update-tool.ts:405-415 (registration)Registers the 'update-tool' on the MCP server with its input schema, output schema, description, and handler function.
export function createTool(server: McpServer) { server.registerTool( TOOL_NAME, { description: TOOL_DESCRIPTION, inputSchema: TOOL_ARGS, outputSchema: OUTPUT_SCHEMA.shape, }, TOOL_HANDLER ); } - src/types/update-tool-types.ts:1-211 (schema)Defines all type schemas: ENTITY_TYPE enum, CameraVideoSettings, CameraAudioSettings, CameraDeviceSettings, TOOL_ARGS (input schema), OUTPUT_SCHEMA, UpdateCameraConfigPayload (API payload), and the parseFacetedUuid helper.
import { z } from "zod"; // Define the entity types that can be updated export const ENTITY_TYPE = z.enum([ "camera", "climate-sensor", "door-controller", "environmental-gateway", "audio-gateway", "doorbell-camera", "badge-reader", ]); export type EntityType = z.infer<typeof ENTITY_TYPE>; // Camera-specific update schemas export const CameraVideoSettings = z.object({ hdr_enabled: z.boolean().optional().describe("Enable HDR (High Dynamic Range)"), img_brightness: z .number() .min(-255) .max(255) .optional() .describe("Image brightness adjustment (-255 to 255)"), img_contrast: z.number().min(0).max(128).optional().describe("Image contrast (0 to 128)"), img_saturation: z.number().min(0).max(255).optional().describe("Image saturation (0 to 255)"), img_sharpness: z.number().min(0).max(255).optional().describe("Image sharpness (0 to 255)"), resolution: z .object({ width: z.number().int().positive().optional(), height: z.number().int().positive().optional(), }) .optional() .describe("Video resolution (width x height)"), wdr_enabled: z.boolean().optional().describe("Enable Wide Dynamic Range"), wdr_strength: z.number().min(0).max(128).optional().describe("WDR strength (0 minimum to 128 maximum)"), video_persist_disabled: z.boolean().optional().describe("Disable video persistence"), zero_motion_video_bitrate_percent: z .number() .min(0) .max(100) .optional() .describe("Zero motion video bitrate percentage"), // Night mode settings night_img_brightness: z .number() .min(-255) .max(255) .optional() .describe("Night mode brightness (-255 to 255)"), night_img_contrast: z .number() .min(0) .max(128) .optional() .describe("Night mode contrast (0 to 128)"), night_img_saturation: z .number() .min(0) .max(255) .optional() .describe("Night mode saturation (0 to 255)"), night_img_sharpness: z .number() .min(0) .max(255) .optional() .describe("Night mode sharpness (0 to 255)"), }); export const CameraAudioSettings = z.object({ audio_record: z.boolean().optional().describe("Enable audio recording"), device_mic_enabled: z.boolean().optional().describe("Enable device microphone"), device_speaker_enabled: z.boolean().optional().describe("Enable device speaker"), audio_internal_mic_volume: z .number() .min(0) .max(100) .optional() .describe("Internal microphone volume (0-100)"), audio_internal_speaker_volume: z .number() .min(0) .max(100) .optional() .describe("Internal speaker volume (0-100)"), }); export const CameraDeviceSettings = z.object({ camera_name: z.string().optional().describe("Camera display name"), camera_timezone: z.string().optional().describe("Camera timezone (e.g., 'America/Los_Angeles')"), led_intensity: z.number().min(0).max(100).optional().describe("LED intensity (0-100)"), led_mode: z .enum(["auto", "always_on", "always_off"]) .optional() .describe("LED mode - use 'always_off' to turn LED off"), led_stealth_mode: z .boolean() .optional() .describe("Enable stealth mode to turn off LED completely - set to true to turn LED off"), }); // Input schema for the tool export const TOOL_ARGS = { entityType: ENTITY_TYPE.describe("Type of entity to update"), entityUuid: z.string().nullable().describe("UUID of the entity to update"), // Camera-specific update fields cameraVideoSettings: z .string() .nullable() .describe("JSON string of video settings to update for camera"), cameraAudioSettings: z .string() .nullable() .describe("JSON string of audio settings to update for camera"), cameraDeviceSettings: z .string() .nullable() .describe("JSON string of device settings to update for camera"), // Step tracking for multi-step updates step: z .enum(["entity-selection", "settings-configuration", "confirmation"]) .nullable() .describe("Current step in the update process"), } as const; const TOOL_ARGS_SCHEMA = z.object(TOOL_ARGS); export type ToolArgs = z.infer<typeof TOOL_ARGS_SCHEMA>; // Output schema export const OUTPUT_SCHEMA = z.object({ needUserInput: z.boolean().optional(), message: z.string().optional(), requestType: z.string().optional(), submitAction: z.string().optional(), entityType: z.string().optional(), entityUuid: z.string().optional(), currentSettings: z.any().optional(), success: z.boolean().optional(), error: z.string().optional(), updatedSettings: z.any().optional(), }); // API payload types export const UpdateCameraConfigPayload = z.object({ configUpdate: z.object({ deviceUuid: z.string(), videoFacetSettings: z .record( z.string(), z.object({ hdr_enabled: z.boolean().nullable().optional(), img_brightness: z.number().nullable().optional(), img_contrast: z.number().nullable().optional(), img_saturation: z.number().nullable().optional(), img_sharpness: z.number().nullable().optional(), resolution: z .object({ width: z.number().nullable().optional(), height: z.number().nullable().optional(), }) .nullable() .optional(), wdr_enabled: z.boolean().nullable().optional(), wdr_strength: z.number().nullable().optional(), video_persist_disabled: z.boolean().nullable().optional(), zero_motion_video_bitrate_percent: z.number().nullable().optional(), night_img_brightness: z.number().nullable().optional(), night_img_contrast: z.number().nullable().optional(), night_img_saturation: z.number().nullable().optional(), night_img_sharpness: z.number().nullable().optional(), }) ) .optional(), audioFacetSettings: z .record( z.string(), z.object({ audio_record: z.boolean().nullable().optional(), device_mic_enabled: z.boolean().nullable().optional(), device_speaker_enabled: z.boolean().nullable().optional(), audio_internal_mic_volume: z.number().nullable().optional(), audio_internal_speaker_volume: z.number().nullable().optional(), }) ) .optional(), deviceSettings: z .object({ camera_name: z.string().nullable().optional(), camera_timezone: z.string().nullable().optional(), led_intensity: z.number().nullable().optional(), led_mode: z.string().nullable().optional(), led_stealth_mode: z.boolean().nullable().optional(), }) .optional(), }), }); export type UpdateCameraConfigPayload = z.infer<typeof UpdateCameraConfigPayload>; // Helper function to parse faceted UUIDs export function parseFacetedUuid(uuid: string): { baseUuid: string; facet: string } { const parts = uuid.split("."); if (parts.length === 2) { return { baseUuid: parts[0], facet: parts[1] }; } // Default to v0 facet if not specified return { baseUuid: uuid, facet: "v0" }; } - src/api/update-tool-api.ts:1-496 (helper)Helper/API layer providing functions for updating camera config, getting camera details, validating feature support (WDR, resolution, audio, set-method-map), cleaning payloads, and formatting settings for display.
import { postApi } from "../network/network.js"; import type { RequestModifiers } from "../util.js"; import type { UpdateCameraConfigPayload } from "../types/update-tool-types.js"; import { schema } from "../types/schema.js"; type CameraConfigOption = { wdrRange?: { min?: number | null; max?: number | null; } | null; resolution?: { width?: number | null; height?: number | null; } | null; } | null; type CameraFeatureValidationResult = { canProceed: boolean; error?: string; }; /** * Updates camera configuration using the faceted config API */ export async function updateCameraConfig( payload: UpdateCameraConfigPayload, requestModifiers?: RequestModifiers, sessionId?: string ): Promise<{ success: boolean; error?: string; updatedSettings?: any; }> { try { const result = await postApi<schema["Common_devices_UpdateConfigWSResponse"]>({ route: "/camera/updateFacetedConfig", body: payload, modifiers: requestModifiers, sessionId, }); if (result.error) { return { success: false, error: result.errorMsg || "Failed to update camera configuration", }; } return { success: true, updatedSettings: payload.configUpdate, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } /** * Gets current camera configuration */ export async function getCameraDetails( cameraUuid: string, requestModifiers?: RequestModifiers, sessionId?: string ): Promise<{ success: boolean; error?: string; data?: any; }> { try { const result = await postApi<any>({ route: "/camera/getDetailsV2", body: { uuid: cameraUuid, }, modifiers: requestModifiers, sessionId, }); if (result.error) { return { success: false, error: result.errorMsg || "Failed to get camera details", }; } return { success: true, data: result.camera, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } async function getCameraHardwareVariation( cameraUuid: string, requestModifiers?: RequestModifiers, sessionId?: string ): Promise<{ success: boolean; error?: string; hwVariation?: string; }> { try { const result = await postApi<schema["Camera_GetCameraDetailsWSResponse"]>({ route: "/camera/getDetails", body: { cameraUuids: [cameraUuid], }, modifiers: requestModifiers, sessionId, }); if (result.error) { return { success: false, error: result.errorMsg || "Failed to get camera hardware variation", }; } const camera = result.cameras?.[0]; return { success: true, hwVariation: camera?.hwVariation ?? undefined, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } async function getOrgCameraConfigOptions( requestModifiers?: RequestModifiers, sessionId?: string ): Promise<{ success: boolean; error?: string; cameraConfigOptions?: Record<string, CameraConfigOption[] | null>; }> { try { const result = await postApi<schema["Org_GetOrgV2WSResponse"]>({ route: "/org/getOrgV2", body: {}, modifiers: requestModifiers, sessionId, }); if (result.error) { return { success: false, error: result.errorMsg || "Failed to get organization camera config options", }; } return { success: true, cameraConfigOptions: (result.cameraConfigOptions as Record< string, CameraConfigOption[] | null > | null) ?? undefined, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } async function getCameraFacetedConfig( cameraUuid: string, requestModifiers?: RequestModifiers, sessionId?: string ): Promise<{ success: boolean; error?: string; config?: schema["Device_config_userconfig_ExternalReadableFacetedUserConfig"]; }> { try { const result = await postApi<schema["Device_config_GetFacetedUserConfigWSResponse"]>({ route: "/camera/getFacetedConfig", body: { deviceUuid: cameraUuid, }, modifiers: requestModifiers, sessionId, }); if (result.error) { return { success: false, error: result.errorMsg || "Failed to get camera faceted config", }; } return { success: true, config: result.config, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }; } } export async function validateCameraFeatureSupport( payload: UpdateCameraConfigPayload, requestModifiers?: RequestModifiers, sessionId?: string ): Promise<CameraFeatureValidationResult> { const requestedVideoSettings = Object.values(payload.configUpdate.videoFacetSettings ?? {}); const requestedAudioSettings = Object.values(payload.configUpdate.audioFacetSettings ?? {}); const requestedDeviceSettings = payload.configUpdate.deviceSettings; if ( requestedVideoSettings.length === 0 && requestedAudioSettings.length === 0 && !requestedDeviceSettings ) { return { canProceed: true }; } const requestedFacet = Object.keys(payload.configUpdate.videoFacetSettings ?? {})[0] ?? Object.keys(payload.configUpdate.audioFacetSettings ?? {})[0] ?? "v0"; const requestedVideoSettingsForFacet = requestedVideoSettings[0]; const requestedAudioSettingsForFacet = requestedAudioSettings[0]; const requestedWdrStrength = requestedVideoSettings .map(settings => settings.wdr_strength) .find(value => value !== undefined && value !== null); const hasWdrRequest = requestedVideoSettings.some( settings => settings.wdr_enabled !== undefined || settings.wdr_strength !== undefined ); const requestedResolution = requestedVideoSettingsForFacet?.resolution; const hasResolutionRequest = requestedResolution?.width !== undefined && requestedResolution?.height !== undefined; const hasAudioRequest = requestedAudioSettings.length > 0; const [hardwareVariationResult, orgOptionsResult, facetedConfigResult] = await Promise.all([ getCameraHardwareVariation(payload.configUpdate.deviceUuid, requestModifiers, sessionId), getOrgCameraConfigOptions(requestModifiers, sessionId), getCameraFacetedConfig(payload.configUpdate.deviceUuid, requestModifiers, sessionId), ]); if (facetedConfigResult.success && facetedConfigResult.config) { const videoFacetConfig = facetedConfigResult.config.videoFacetSettings?.[requestedFacet] as | (schema["Device_config_settings_ExternalReadableVideoSettings"] & { updatedSetMethodMap?: Record<string, boolean | null> | null; }) | undefined; const audioFacetConfig = facetedConfigResult.config.audioFacetSettings?.[requestedFacet] as | (schema["Device_config_settings_ExternalReadableAudioSettings"] & { updatedSetMethodMap?: Record<string, boolean | null> | null; }) | undefined; const deviceConfig = facetedConfigResult.config.deviceSettings as | (schema["Device_config_settings_ExternalReadableDeviceSettings"] & { updatedSetMethodMap?: Record<string, boolean | null> | null; }) | undefined; if (hasAudioRequest && audioFacetConfig?.audio_supported === false) { return { canProceed: false, error: "This camera does not support audio features.", }; } const unsupportedVideoFields = getUnsupportedFieldsFromSetMethodMap( requestedVideoSettingsForFacet, videoFacetConfig?.updatedSetMethodMap ); if (unsupportedVideoFields.length > 0) { return { canProceed: false, error: `This camera does not support updating video setting: ${unsupportedVideoFields[0]}.`, }; } const unsupportedAudioFields = getUnsupportedFieldsFromSetMethodMap( requestedAudioSettingsForFacet, audioFacetConfig?.updatedSetMethodMap ); if (unsupportedAudioFields.length > 0) { return { canProceed: false, error: `This camera does not support updating audio setting: ${unsupportedAudioFields[0]}.`, }; } const unsupportedDeviceFields = getUnsupportedFieldsFromSetMethodMap( requestedDeviceSettings, deviceConfig?.updatedSetMethodMap ); if (unsupportedDeviceFields.length > 0) { return { canProceed: false, error: `This camera does not support updating device setting: ${unsupportedDeviceFields[0]}.`, }; } } // Model-level capability metadata can be unavailable on some deployments; do not hard-fail. if (!hardwareVariationResult.success || !orgOptionsResult.success) { return { canProceed: true }; } const hwVariation = hardwareVariationResult.hwVariation; if (!hwVariation) { return { canProceed: true }; } const cameraOptions = orgOptionsResult.cameraConfigOptions?.[hwVariation]; if ( (!hasWdrRequest && !hasResolutionRequest) || !Array.isArray(cameraOptions) || cameraOptions.length === 0 ) { return { canProceed: true }; } if (hasWdrRequest) { const wdrOption = cameraOptions.find(option => option?.wdrRange !== undefined); if (!wdrOption?.wdrRange) { return { canProceed: false, error: "This camera does not support WDR feature.", }; } if (typeof requestedWdrStrength === "number") { const min = wdrOption.wdrRange.min; const max = wdrOption.wdrRange.max; if (typeof min === "number" && requestedWdrStrength < min) { return { canProceed: false, error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} is below the supported minimum (${min}).`, }; } if (typeof max === "number" && requestedWdrStrength > max) { return { canProceed: false, error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} exceeds the supported maximum (${max}).`, }; } } } if (hasResolutionRequest) { const isRequestedResolutionSupported = cameraOptions.some(option => { const optionWidth = option?.resolution?.width; const optionHeight = option?.resolution?.height; return ( typeof optionWidth === "number" && typeof optionHeight === "number" && optionWidth === requestedResolution.width && optionHeight === requestedResolution.height ); }); if (!isRequestedResolutionSupported) { return { canProceed: false, error: `This camera does not support resolution ${requestedResolution.width}x${requestedResolution.height}.`, }; } } return { canProceed: true }; } function getUnsupportedFieldsFromSetMethodMap( requestedSettings: Record<string, unknown> | undefined, setMethodMap: Record<string, boolean | null> | null | undefined ): string[] { if (!requestedSettings || !setMethodMap) { return []; } return Object.keys(requestedSettings).filter(field => setMethodMap[field] === false); } /** * Helper function to remove null/undefined fields from update payload */ export function cleanUpdatePayload(payload: any): any { const cleaned: any = {}; for (const [key, value] of Object.entries(payload)) { if (value !== null && value !== undefined) { if (typeof value === "object" && !Array.isArray(value)) { const cleanedValue = cleanUpdatePayload(value); if (Object.keys(cleanedValue).length > 0) { cleaned[key] = cleanedValue; } } else { cleaned[key] = value; } } } return cleaned; } /** * Formats camera settings for display */ export function formatCameraSettings(settings: any): string { const sections: string[] = []; if (settings.videoFacetSettings) { const videoSettings = Object.values(settings.videoFacetSettings)[0] as any; if (videoSettings) { const videoItems: string[] = []; if (videoSettings.resolution) videoItems.push( `Resolution: ${videoSettings.resolution.width}x${videoSettings.resolution.height}` ); if (videoSettings.img_brightness !== undefined) videoItems.push(`Brightness: ${videoSettings.img_brightness}`); if (videoSettings.img_contrast !== undefined) videoItems.push(`Contrast: ${videoSettings.img_contrast}`); if (videoSettings.img_saturation !== undefined) videoItems.push(`Saturation: ${videoSettings.img_saturation}`); if (videoSettings.img_sharpness !== undefined) videoItems.push(`Sharpness: ${videoSettings.img_sharpness}`); if (videoSettings.hdr_enabled !== undefined) videoItems.push(`HDR: ${videoSettings.hdr_enabled ? "On" : "Off"}`); if (videoSettings.wdr_enabled !== undefined) videoItems.push(`WDR: ${videoSettings.wdr_enabled ? "On" : "Off"}`); if (videoSettings.wdr_strength !== undefined) videoItems.push(`WDR Strength: ${videoSettings.wdr_strength}`); if (videoSettings.video_persist_disabled !== undefined) videoItems.push( `Video Persist: ${videoSettings.video_persist_disabled ? "Disabled" : "Enabled"}` ); if (videoSettings.zero_motion_video_bitrate_percent !== undefined) videoItems.push(`Zero Motion Bitrate: ${videoSettings.zero_motion_video_bitrate_percent}%`); if (videoItems.length > 0) { sections.push("**Video Settings:**\n" + videoItems.map(item => `• ${item}`).join("\n")); } } } if (settings.audioFacetSettings) { const audioSettings = Object.values(settings.audioFacetSettings)[0] as any; if (audioSettings) { const audioItems: string[] = []; if (audioSettings.audio_record !== undefined) audioItems.push(`Recording: ${audioSettings.audio_record ? "On" : "Off"}`); if (audioSettings.device_mic_enabled !== undefined) audioItems.push(`Microphone: ${audioSettings.device_mic_enabled ? "Enabled" : "Disabled"}`); if (audioSettings.device_speaker_enabled !== undefined) audioItems.push( `Speaker: ${audioSettings.device_speaker_enabled ? "Enabled" : "Disabled"}` ); if (audioItems.length > 0) { sections.push("**Audio Settings:**\n" + audioItems.map(item => `• ${item}`).join("\n")); } } } if (settings.deviceSettings) { const deviceItems: string[] = []; if (settings.deviceSettings.camera_name) deviceItems.push(`Name: ${settings.deviceSettings.camera_name}`); if (settings.deviceSettings.camera_timezone) deviceItems.push(`Timezone: ${settings.deviceSettings.camera_timezone}`); if (settings.deviceSettings.led_mode) deviceItems.push(`LED Mode: ${settings.deviceSettings.led_mode}`); if (settings.deviceSettings.led_intensity !== undefined) deviceItems.push(`LED Intensity: ${settings.deviceSettings.led_intensity}`); if (settings.deviceSettings.led_stealth_mode !== undefined) deviceItems.push( `LED Stealth Mode: ${settings.deviceSettings.led_stealth_mode ? "On" : "Off"}` ); if (deviceItems.length > 0) { sections.push("**Device Settings:**\n" + deviceItems.map(item => `• ${item}`).join("\n")); } } return sections.join("\n\n"); } - src/tools/update-tool.ts:386-403 (helper)Helper function that checks if the payload contains settings that depend on camera capabilities (resolution, HDR, WDR, audio recording, mic, speaker).
function hasCapabilitySensitiveSettings(payload: UpdateCameraConfigPayload): boolean { const requestedVideoSettings = Object.values(payload.configUpdate.videoFacetSettings ?? {}); const hasCapabilityDependentVideoChange = requestedVideoSettings.some( settings => settings.resolution !== undefined || settings.hdr_enabled !== undefined || settings.wdr_enabled !== undefined || settings.wdr_strength !== undefined ); const requestedAudioSettings = Object.values(payload.configUpdate.audioFacetSettings ?? {}); const hasCapabilityDependentAudioChange = requestedAudioSettings.some( settings => settings.audio_record !== undefined || settings.device_mic_enabled !== undefined || settings.device_speaker_enabled !== undefined ); return hasCapabilityDependentVideoChange || hasCapabilityDependentAudioChange; }