Files
Alta-Proxy-Tool/renderer.js
T
peji cccb20bd31 Update app icon to Avigilon Alta logo, remove Test Connection button, rename proxy buttons
- Replace app icon (assets/icon.png) with Avigilon Alta "A" logo
- Add Windows .ico version of the icon
- Remove Test Connection button from API Connection section
- Rename Start/Stop Cookie Proxy buttons to Start/Stop Proxy
- Add .claude/ and nul to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 22:03:51 -05:00

477 lines
16 KiB
JavaScript

// Global variables to store session data
let sessionData = {
deploymentUrl: '',
cookies: null,
isConnected: false
};
// DOM elements
const connectionStatus = document.getElementById('connectionStatus');
const deviceStatus = document.getElementById('deviceStatus');
const deviceList = document.getElementById('deviceList');
const statusIndicator = document.getElementById('statusIndicator');
const deviceSearch = document.getElementById('deviceSearch');
// Connection buttons
const disconnectBtn = document.getElementById('disconnectBtn');
// Cookie proxy elements
const cookieDeviceUUID = document.getElementById('cookieDeviceUUID');
const cookieKey = document.getElementById('cookieKey');
const startCookieProxyBtn = document.getElementById('startCookieProxyBtn');
const stopCookieProxyBtn = document.getElementById('stopCookieProxyBtn');
// Track selected device
let selectedDevice = null;
let activeCookieProxyConnections = new Map(); // Track cookie-based proxy connections
let allDevices = []; // Store all devices for search functionality
// Event listeners
disconnectBtn.addEventListener('click', handleDisconnect);
// Cookie proxy event listeners
startCookieProxyBtn.addEventListener('click', handleStartCookieProxy);
stopCookieProxyBtn.addEventListener('click', handleStopCookieProxy);
// Cookie key input listener to update button states
cookieKey.addEventListener('input', updateCookieProxyButtonStates);
// Device search event listener
deviceSearch.addEventListener('input', handleDeviceSearch);
// Handle disconnect
function handleDisconnect() {
sessionData.isConnected = false;
sessionData.cookies = null;
sessionData.deploymentUrl = '';
selectedDevice = null;
activeCookieProxyConnections.clear();
allDevices = []; // Clear stored devices
updateConnectionStatus(false);
updateButtonStates();
clearDeviceList();
cookieDeviceUUID.value = '';
cookieKey.value = '';
deviceSearch.value = ''; // Clear search input
// Clear device status message
deviceStatus.style.display = 'none';
deviceStatus.textContent = '';
showStatus(connectionStatus, 'Disconnected from API', 'info');
}
// Handle start cookie proxy
async function handleStartCookieProxy() {
if (!selectedDevice) {
showStatus(connectionStatus, 'Please select a device first', 'error');
return;
}
const cookieKeyValue = cookieKey.value.trim();
if (!cookieKeyValue) {
showStatus(connectionStatus, 'Please enter a cookie key', 'error');
return;
}
if (!sessionData.deploymentUrl) {
showStatus(connectionStatus, 'Please connect to API first to get deployment URL', 'error');
return;
}
// Check if this device already has an active cookie connection
const deviceId = selectedDevice.guid || selectedDevice.id;
if (activeCookieProxyConnections.has(deviceId)) {
showStatus(connectionStatus, `Cookie proxy already running for device ${selectedDevice.name}`, 'warning');
return;
}
// Disable button during launch
startCookieProxyBtn.disabled = true;
showStatus(connectionStatus, `Starting cookie proxy for device ${selectedDevice.name}...`, 'info');
try {
const result = await window.electronAPI.launchCookieCameraProxy({
deploymentUrl: sessionData.deploymentUrl,
cookieKey: cookieKeyValue,
deviceUuid: deviceId
});
if (result.success) {
// Track this connection
activeCookieProxyConnections.set(deviceId, {
processId: result.processId,
deviceName: selectedDevice.name,
deviceId: deviceId,
startTime: Date.now(),
type: 'cookie'
});
updateCookieProxyButtonStates();
showStatus(connectionStatus, `${result.message} (Cookie proxy active for ${selectedDevice.name})`, 'success');
} else {
showStatus(connectionStatus, `Failed to start cookie proxy: ${result.message}`, 'error');
}
} catch (error) {
console.error('Cookie proxy launch error:', error);
showStatus(connectionStatus, `Error launching cookie proxy: ${error.message}`, 'error');
} finally {
startCookieProxyBtn.disabled = false;
}
}
// Handle stop cookie proxy
async function handleStopCookieProxy() {
if (activeCookieProxyConnections.size === 0) {
showStatus(connectionStatus, 'No active cookie proxy connections found', 'warning');
return;
}
showStatus(connectionStatus, 'Stopping cookie proxy connections...', 'info');
try {
const result = await window.electronAPI.stopCameraProxy(null); // Stop all processes
if (result.success) {
activeCookieProxyConnections.clear();
showStatus(connectionStatus, 'Cookie proxy connections stopped successfully', 'success');
// Update visual indicators for all devices
const deviceItems = deviceList.querySelectorAll('.device-item');
deviceItems.forEach(item => {
item.classList.remove('cookie-proxy-active');
});
} else {
showStatus(connectionStatus, `Failed to stop cookie proxy: ${result.message}`, 'warning');
}
} catch (error) {
console.error('Stop cookie proxy error:', error);
showStatus(connectionStatus, 'Error stopping cookie proxy', 'error');
}
updateCookieProxyButtonStates();
}
// Update cookie proxy button states
function updateCookieProxyButtonStates() {
const hasSelectedDevice = selectedDevice !== null;
const hasCookieKey = cookieKey.value.trim().length > 0;
const hasActiveConnection = activeCookieProxyConnections.size > 0;
if (sessionData.isConnected && hasSelectedDevice && hasCookieKey) {
const deviceId = selectedDevice.guid || selectedDevice.id;
const isThisDeviceActive = activeCookieProxyConnections.has(deviceId);
startCookieProxyBtn.disabled = isThisDeviceActive;
stopCookieProxyBtn.disabled = !hasActiveConnection;
} else {
startCookieProxyBtn.disabled = true;
stopCookieProxyBtn.disabled = !hasActiveConnection;
}
}
// Update connection status indicator
function updateConnectionStatus(connected) {
const statusDot = statusIndicator.querySelector('.status-dot');
const statusText = statusIndicator.querySelector('.status-text');
if (connected) {
statusDot.className = 'status-dot online';
statusText.textContent = 'Connected';
} else {
statusDot.className = 'status-dot offline';
statusText.textContent = 'Disconnected';
}
}
// Update button states based on connection status
function updateButtonStates() {
if (sessionData.isConnected) {
disconnectBtn.disabled = false;
} else {
disconnectBtn.disabled = true;
startCookieProxyBtn.disabled = true;
stopCookieProxyBtn.disabled = true;
}
// Always update cookie proxy button states
updateCookieProxyButtonStates();
}
// Handle get devices (now called automatically)
async function handleGetDevices() {
if (!sessionData.isConnected) {
showStatus(deviceStatus, 'Please connect to the API first', 'error');
return;
}
showStatus(deviceStatus, 'Fetching devices...', 'info');
clearDeviceList();
try {
const result = await window.electronAPI.getDevices({
deploymentUrl: sessionData.deploymentUrl,
cookies: sessionData.cookies
});
if (result.success) {
// Filter devices to only show non-cloud cameras (localStorage = false)
const filteredDevices = result.devices.filter(device => {
// Check if device has capabilities and localStorage property
if (device.capabilities && device.capabilities.localStorage !== undefined) {
// Only show devices where localStorage is false (non-cloud cameras)
return device.capabilities.localStorage === false;
}
// If no capabilities or localStorage property, include the device (fallback)
return true;
});
const totalDevices = result.devices.length;
const filteredCount = filteredDevices.length;
const cloudDevicesHidden = totalDevices - filteredCount;
let statusMessage = `Found ${filteredCount} local camera${filteredCount !== 1 ? 's' : ''}`;
if (cloudDevicesHidden > 0) {
statusMessage += ` (${cloudDevicesHidden} cloud camera${cloudDevicesHidden !== 1 ? 's' : ''} hidden)`;
}
showStatus(deviceStatus, statusMessage, 'success');
// Store all devices for search functionality
allDevices = filteredDevices;
displayDevices(filteredDevices);
} else {
showStatus(deviceStatus, `Failed to get devices: ${result.message}`, 'error');
}
} catch (error) {
console.error('Get devices error:', error);
showStatus(deviceStatus, `Error getting devices: ${error.message}`, 'error');
}
}
// Helper function to determine device status from API data
function getDeviceStatus(device) {
// Check for live.display_status first (Alta API standard)
if (device.live && device.live.display_status) {
const status = device.live.display_status.toLowerCase();
// Handle color-based status responses from Alta API
if (status === 'green') {
return {
isOnline: true,
statusText: 'Online'
};
} else if (status === 'red') {
return {
isOnline: false,
statusText: 'Offline'
};
} else if (status === 'yellow' || status === 'orange') {
return {
isOnline: false,
statusText: 'Warning'
};
}
// Handle text-based status responses
return {
isOnline: status === 'online' || status === 'live' || status === 'connected',
statusText: status === 'online' || status === 'live' || status === 'connected' ? 'Online' : 'Offline'
};
}
// Fallback to other possible status fields
if (device.online !== undefined) {
return {
isOnline: device.online,
statusText: device.online ? 'Online' : 'Offline'
};
}
if (device.status) {
const status = device.status.toLowerCase();
// Handle color-based status in other fields
if (status === 'green') {
return {
isOnline: true,
statusText: 'Online'
};
} else if (status === 'red') {
return {
isOnline: false,
statusText: 'Offline'
};
}
return {
isOnline: status === 'online' || status === 'live' || status === 'connected',
statusText: status === 'online' || status === 'live' || status === 'connected' ? 'Online' : 'Offline'
};
}
// Default to offline if no status information available
return {
isOnline: false,
statusText: 'Offline'
};
}
// Handle device search
function handleDeviceSearch() {
const searchTerm = deviceSearch.value.toLowerCase().trim();
if (!searchTerm) {
// Show all devices if search is empty
displayDevices(allDevices);
return;
}
// Filter devices based on search term
const filteredDevices = allDevices.filter(device => {
const deviceName = (device.name || '').toLowerCase();
const deviceId = (device.guid || device.id || '').toLowerCase();
const deviceType = (device.type || '').toLowerCase();
const deviceModel = (device.model || '').toLowerCase();
const deviceIp = (device.ipAddress || '').toLowerCase();
return deviceName.includes(searchTerm) ||
deviceId.includes(searchTerm) ||
deviceType.includes(searchTerm) ||
deviceModel.includes(searchTerm) ||
deviceIp.includes(searchTerm);
});
displayDevices(filteredDevices);
}
// Display devices in the UI
function displayDevices(devices) {
clearDeviceList();
if (!devices || devices.length === 0) {
const searchTerm = deviceSearch.value.toLowerCase().trim();
const message = searchTerm ? 'No devices match your search' : 'No devices found';
deviceList.innerHTML = `<p class="no-devices">${message}</p>`;
return;
}
devices.forEach((device, index) => {
const deviceItem = document.createElement('div');
deviceItem.className = 'device-item';
deviceItem.dataset.deviceIndex = index;
deviceItem.dataset.deviceId = device.guid || device.id;
const deviceStatus = getDeviceStatus(device);
const deviceId = device.guid || device.id;
const isCookieProxyActive = activeCookieProxyConnections.has(deviceId);
// Add cookie-proxy-active class if this device has an active cookie connection
if (isCookieProxyActive) {
deviceItem.classList.add('cookie-proxy-active');
}
deviceItem.innerHTML = `
<div class="device-name">${escapeHtml(device.name || 'Unnamed Device')}</div>
<div class="device-status-dot ${deviceStatus.isOnline ? 'online' : 'offline'}"></div>
`;
// Add click handler for device selection
deviceItem.addEventListener('click', () => selectDevice(device, deviceItem));
deviceList.appendChild(deviceItem);
});
// Update cookie proxy button states after displaying devices
updateCookieProxyButtonStates();
}
// Handle device selection
function selectDevice(device, deviceElement) {
// Remove previous selection
const previousSelected = deviceList.querySelector('.device-item.selected');
if (previousSelected) {
previousSelected.classList.remove('selected');
}
// Select current device
deviceElement.classList.add('selected');
selectedDevice = device;
// Auto-populate cookie device UUID field
const uuid = device.guid || device.id || '';
cookieDeviceUUID.value = uuid;
// Show device selection feedback with connection status
if (uuid) {
const isCookieActive = activeCookieProxyConnections.has(uuid);
const cookieStatusText = isCookieActive ? ' (COOKIE PROXY ACTIVE)' : '';
showStatus(connectionStatus, `Selected device: ${device.name || 'Unnamed Device'} (UUID: ${uuid})${cookieStatusText}`, 'info');
}
updateCookieProxyButtonStates();
}
// Utility functions
function showStatus(element, message, type) {
element.textContent = message;
element.className = `status-message ${type}`;
element.style.display = 'block';
}
function clearDeviceList() {
deviceList.innerHTML = '<p class="placeholder-text">Connect to API to load devices</p>';
selectedDevice = null;
}
function escapeHtml(text) {
if (typeof text !== 'string') return text;
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Handle cookie received from Chrome extension via local HTTP bridge
async function handleExtensionCookie(data) {
const { deploymentUrl, cookies, cookieValue } = data;
// If already connected, disconnect first
if (sessionData.isConnected) {
handleDisconnect();
}
// Set session state from extension cookie
sessionData.deploymentUrl = deploymentUrl;
sessionData.cookies = cookies;
sessionData.isConnected = true;
showStatus(connectionStatus, `Connected via Chrome extension to ${deploymentUrl}`, 'success');
updateConnectionStatus(true);
updateButtonStates();
// Auto-populate cookie key
cookieKey.value = cookieValue;
updateCookieProxyButtonStates();
// Fetch devices
try {
await handleGetDevices();
} catch (err) {
console.error('Failed to fetch devices after extension cookie:', err);
showStatus(deviceStatus, 'Connected, but failed to load devices.', 'warning');
}
}
// Initialize the app
document.addEventListener('DOMContentLoaded', async () => {
console.log('Alta Video Camera Proxy loaded');
// Initialize connection status
updateConnectionStatus(false);
updateButtonStates();
// Listen for cookies pushed from Chrome extension
window.electronAPI.onExtensionCookie(handleExtensionCookie);
});