Bahajacom2 commited on
Commit
f35f899
·
verified ·
1 Parent(s): dcb8cda

Je souhaiterais créer une page web permettant de contrôler deux caméras PTZ (thermique et jour), d’afficher le flux RTSP de chacune, d’intégrer un bouton de configuration et un bouton pour basculer entre les deux caméras, tout en envoyant les commandes(clique et release) et en récupérant les informations de position (PAN, TILT, ZOOM) via une application Java desktop utilisant un WebSocketServer.

Browse files
Files changed (2) hide show
  1. README.md +8 -5
  2. index.html +360 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Ptz Camera Control Hub
3
- emoji: 📈
4
- colorFrom: pink
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: PTZ Camera Control Hub 🎥
3
+ colorFrom: gray
4
+ colorTo: gray
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://deepsite.hf.co).
index.html CHANGED
@@ -1,19 +1,361 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PTZ Camera Control Hub</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/feather-icons"></script>
9
+ <style>
10
+ .camera-feed {
11
+ background-color: #1a202c;
12
+ aspect-ratio: 16/9;
13
+ }
14
+ .position-indicator {
15
+ background-color: rgba(0, 0, 0, 0.7);
16
+ }
17
+ .control-btn {
18
+ transition: all 0.2s;
19
+ }
20
+ .control-btn:active {
21
+ transform: scale(0.95);
22
+ }
23
+ </style>
24
+ </head>
25
+ <body class="bg-gray-900 text-white min-h-screen">
26
+ <!-- Header -->
27
+ <header class="bg-gray-800 py-4 px-6 shadow-lg">
28
+ <div class="container mx-auto flex justify-between items-center">
29
+ <h1 class="text-2xl font-bold flex items-center">
30
+ <i data-feather="video" class="mr-2"></i>
31
+ PTZ Camera Control Hub
32
+ </h1>
33
+ <button id="configBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg flex items-center">
34
+ <i data-feather="settings" class="mr-2"></i>
35
+ Configuration
36
+ </button>
37
+ </div>
38
+ </header>
39
+
40
+ <!-- Main Content -->
41
+ <main class="container mx-auto py-6 px-4">
42
+ <!-- Camera Toggle -->
43
+ <div class="flex justify-center mb-6">
44
+ <div class="inline-flex rounded-md shadow-sm">
45
+ <button id="dayCamBtn" class="px-4 py-2 text-sm font-medium rounded-l-lg bg-gray-700 hover:bg-gray-600 focus:z-10 focus:ring-2 focus:ring-blue-500">
46
+ Day Camera
47
+ </button>
48
+ <button id="thermalCamBtn" class="px-4 py-2 text-sm font-medium rounded-r-md bg-gray-700 hover:bg-gray-600 focus:z-10 focus:ring-2 focus:ring-blue-500">
49
+ Thermal Camera
50
+ </button>
51
+ </div>
52
+ </div>
53
+
54
+ <!-- Camera Feeds -->
55
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
56
+ <!-- Day Camera Feed -->
57
+ <div class="camera-container" id="dayCameraContainer">
58
+ <div class="camera-feed rounded-lg overflow-hidden relative">
59
+ <video id="dayCamFeed" class="w-full h-full" autoplay muted></video>
60
+ <div class="position-indicator absolute bottom-0 left-0 right-0 p-2 text-sm">
61
+ <div class="flex justify-between">
62
+ <span>PAN: <span id="dayPan">0°</span></span>
63
+ <span>TILT: <span id="dayTilt">0°</span></span>
64
+ <span>ZOOM: <span id="dayZoom">1x</span></span>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ <div class="controls grid grid-cols-3 gap-2 mt-2">
69
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="pan-left">
70
+ <i data-feather="arrow-left"></i>
71
+ </button>
72
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="tilt-up">
73
+ <i data-feather="arrow-up"></i>
74
+ </button>
75
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="zoom-in">
76
+ <i data-feather="zoom-in"></i>
77
+ </button>
78
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="pan-right">
79
+ <i data-feather="arrow-right"></i>
80
+ </button>
81
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="tilt-down">
82
+ <i data-feather="arrow-down"></i>
83
+ </button>
84
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="zoom-out">
85
+ <i data-feather="zoom-out"></i>
86
+ </button>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Thermal Camera Feed -->
91
+ <div class="camera-container hidden" id="thermalCameraContainer">
92
+ <div class="camera-feed rounded-lg overflow-hidden relative">
93
+ <video id="thermalCamFeed" class="w-full h-full" autoplay muted></video>
94
+ <div class="position-indicator absolute bottom-0 left-0 right-0 p-2 text-sm">
95
+ <div class="flex justify-between">
96
+ <span>PAN: <span id="thermalPan">0°</span></span>
97
+ <span>TILT: <span id="thermalTilt">0°</span></span>
98
+ <span>ZOOM: <span id="thermalZoom">1x</span></span>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ <div class="controls grid grid-cols-3 gap-2 mt-2">
103
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="pan-left">
104
+ <i data-feather="arrow-left"></i>
105
+ </button>
106
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="tilt-up">
107
+ <i data-feather="arrow-up"></i>
108
+ </button>
109
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="zoom-in">
110
+ <i data-feather="zoom-in"></i>
111
+ </button>
112
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="pan-right">
113
+ <i data-feather="arrow-right"></i>
114
+ </button>
115
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="tilt-down">
116
+ <i data-feather="arrow-down"></i>
117
+ </button>
118
+ <button class="control-btn bg-gray-700 hover:bg-gray-600 p-2 rounded" data-command="zoom-out">
119
+ <i data-feather="zoom-out"></i>
120
+ </button>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <!-- Status Bar -->
126
+ <div class="bg-gray-800 rounded-lg p-4 text-sm">
127
+ <div class="flex items-center">
128
+ <div id="connectionStatus" class="flex items-center mr-4">
129
+ <span class="w-3 h-3 rounded-full bg-red-500 mr-2"></span>
130
+ <span>Disconnected</span>
131
+ </div>
132
+ <div id="activeCamera" class="flex items-center">
133
+ <i data-feather="video" class="mr-2"></i>
134
+ <span>Day Camera</span>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </main>
139
+
140
+ <!-- Configuration Modal -->
141
+ <div id="configModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
142
+ <div class="bg-gray-800 rounded-lg p-6 w-full max-w-md">
143
+ <div class="flex justify-between items-center mb-4">
144
+ <h2 class="text-xl font-bold">Configuration</h2>
145
+ <button id="closeConfigBtn" class="text-gray-400 hover:text-white">
146
+ <i data-feather="x"></i>
147
+ </button>
148
+ </div>
149
+
150
+ <div class="space-y-4">
151
+ <div>
152
+ <label class="block text-sm font-medium mb-1">Day Camera RTSP URL</label>
153
+ <input type="text" id="dayCamUrl" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2">
154
+ </div>
155
+ <div>
156
+ <label class="block text-sm font-medium mb-1">Thermal Camera RTSP URL</label>
157
+ <input type="text" id="thermalCamUrl" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2">
158
+ </div>
159
+ <div>
160
+ <label class="block text-sm font-medium mb-1">WebSocket Server URL</label>
161
+ <input type="text" id="wsUrl" value="ws://localhost:8080" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2">
162
+ </div>
163
+ </div>
164
+
165
+ <div class="mt-6 flex justify-end space-x-3">
166
+ <button id="saveConfigBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg">
167
+ Save
168
+ </button>
169
+ <button id="cancelConfigBtn" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg">
170
+ Cancel
171
+ </button>
172
+ </div>
173
+ </div>
174
+ </div>
175
+
176
+ <script>
177
+ feather.replace();
178
+
179
+ // WebSocket connection
180
+ let socket;
181
+
182
+ // DOM Elements
183
+ const dayCamBtn = document.getElementById('dayCamBtn');
184
+ const thermalCamBtn = document.getElementById('thermalCamBtn');
185
+ const dayCameraContainer = document.getElementById('dayCameraContainer');
186
+ const thermalCameraContainer = document.getElementById('thermalCameraContainer');
187
+ const configBtn = document.getElementById('configBtn');
188
+ const configModal = document.getElementById('configModal');
189
+ const closeConfigBtn = document.getElementById('closeConfigBtn');
190
+ const saveConfigBtn = document.getElementById('saveConfigBtn');
191
+ const cancelConfigBtn = document.getElementById('cancelConfigBtn');
192
+ const connectionStatus = document.getElementById('connectionStatus');
193
+ const activeCamera = document.getElementById('activeCamera');
194
+
195
+ // Camera control buttons
196
+ const controlButtons = document.querySelectorAll('.control-btn');
197
+
198
+ // Initialize camera feeds
199
+ const dayCamFeed = document.getElementById('dayCamFeed');
200
+ const thermalCamFeed = document.getElementById('thermalCamFeed');
201
+
202
+ // Toggle between cameras
203
+ dayCamBtn.addEventListener('click', () => {
204
+ dayCameraContainer.classList.remove('hidden');
205
+ thermalCameraContainer.classList.add('hidden');
206
+ dayCamBtn.classList.add('bg-blue-600');
207
+ thermalCamBtn.classList.remove('bg-blue-600');
208
+ activeCamera.innerHTML = '<i data-feather="video" class="mr-2"></i><span>Day Camera</span>';
209
+ feather.replace();
210
+ });
211
+
212
+ thermalCamBtn.addEventListener('click', () => {
213
+ dayCameraContainer.classList.add('hidden');
214
+ thermalCameraContainer.classList.remove('hidden');
215
+ dayCamBtn.classList.remove('bg-blue-600');
216
+ thermalCamBtn.classList.add('bg-blue-600');
217
+ activeCamera.innerHTML = '<i data-feather="video" class="mr-2"></i><span>Thermal Camera</span>';
218
+ feather.replace();
219
+ });
220
+
221
+ // Configuration modal
222
+ configBtn.addEventListener('click', () => {
223
+ configModal.classList.remove('hidden');
224
+ });
225
+
226
+ closeConfigBtn.addEventListener('click', () => {
227
+ configModal.classList.add('hidden');
228
+ });
229
+
230
+ cancelConfigBtn.addEventListener('click', () => {
231
+ configModal.classList.add('hidden');
232
+ });
233
+
234
+ saveConfigBtn.addEventListener('click', () => {
235
+ const dayCamUrl = document.getElementById('dayCamUrl').value;
236
+ const thermalCamUrl = document.getElementById('thermalCamUrl').value;
237
+ const wsUrl = document.getElementById('wsUrl').value;
238
+
239
+ // Save to localStorage
240
+ localStorage.setItem('dayCamUrl', dayCamUrl);
241
+ localStorage.setItem('thermalCamUrl', thermalCamUrl);
242
+ localStorage.setItem('wsUrl', wsUrl);
243
+
244
+ // Connect to WebSocket
245
+ connectWebSocket(wsUrl);
246
+
247
+ // Load camera feeds
248
+ if (dayCamUrl) {
249
+ dayCamFeed.src = dayCamUrl;
250
+ }
251
+
252
+ if (thermalCamUrl) {
253
+ thermalCamFeed.src = thermalCamUrl;
254
+ }
255
+
256
+ configModal.classList.add('hidden');
257
+ });
258
+
259
+ // Control button events
260
+ controlButtons.forEach(button => {
261
+ button.addEventListener('mousedown', () => {
262
+ const command = button.getAttribute('data-command');
263
+ sendCommand(command, 'start');
264
+ });
265
+
266
+ button.addEventListener('mouseup', () => {
267
+ const command = button.getAttribute('data-command');
268
+ sendCommand(command, 'stop');
269
+ });
270
+
271
+ button.addEventListener('mouseleave', () => {
272
+ const command = button.getAttribute('data-command');
273
+ sendCommand(command, 'stop');
274
+ });
275
+ });
276
+
277
+ // WebSocket functions
278
+ function connectWebSocket(url) {
279
+ if (socket) {
280
+ socket.close();
281
+ }
282
+
283
+ socket = new WebSocket(url);
284
+
285
+ socket.onopen = () => {
286
+ connectionStatus.innerHTML = '<span class="w-3 h-3 rounded-full bg-green-500 mr-2"></span><span>Connected</span>';
287
+ console.log('WebSocket connected');
288
+ };
289
+
290
+ socket.onclose = () => {
291
+ connectionStatus.innerHTML = '<span class="w-3 h-3 rounded-full bg-red-500 mr-2"></span><span>Disconnected</span>';
292
+ console.log('WebSocket disconnected');
293
+ };
294
+
295
+ socket.onerror = (error) => {
296
+ console.error('WebSocket error:', error);
297
+ };
298
+
299
+ socket.onmessage = (event) => {
300
+ const data = JSON.parse(event.data);
301
+ console.log('Received:', data);
302
+
303
+ // Update position indicators
304
+ if (data.type === 'position') {
305
+ if (data.camera === 'day') {
306
+ document.getElementById('dayPan').textContent = `${data.pan}°`;
307
+ document.getElementById('dayTilt').textContent = `${data.tilt}°`;
308
+ document.getElementById('dayZoom').textContent = `${data.zoom}x`;
309
+ } else if (data.camera === 'thermal') {
310
+ document.getElementById('thermalPan').textContent = `${data.pan}°`;
311
+ document.getElementById('thermalTilt').textContent = `${data.tilt}°`;
312
+ document.getElementById('thermalZoom').textContent = `${data.zoom}x`;
313
+ }
314
+ }
315
+ };
316
+ }
317
+
318
+ function sendCommand(command, action) {
319
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
320
+ console.error('WebSocket is not connected');
321
+ return;
322
+ }
323
+
324
+ const activeCamera = dayCameraContainer.classList.contains('hidden') ? 'thermal' : 'day';
325
+ const message = {
326
+ type: 'control',
327
+ camera: activeCamera,
328
+ command: command,
329
+ action: action
330
+ };
331
+
332
+ socket.send(JSON.stringify(message));
333
+ console.log('Sent:', message);
334
+ }
335
+
336
+ // Load saved configuration
337
+ function loadConfig() {
338
+ const dayCamUrl = localStorage.getItem('dayCamUrl');
339
+ const thermalCamUrl = localStorage.getItem('thermalCamUrl');
340
+ const wsUrl = localStorage.getItem('wsUrl') || 'ws://localhost:8080';
341
+
342
+ if (dayCamUrl) {
343
+ document.getElementById('dayCamUrl').value = dayCamUrl;
344
+ dayCamFeed.src = dayCamUrl;
345
+ }
346
+
347
+ if (thermalCamUrl) {
348
+ document.getElementById('thermalCamUrl').value = thermalCamUrl;
349
+ thermalCamFeed.src = thermalCamUrl;
350
+ }
351
+
352
+ document.getElementById('wsUrl').value = wsUrl;
353
+ connectWebSocket(wsUrl);
354
+ }
355
+
356
+ // Initialize
357
+ loadConfig();
358
+ feather.replace();
359
+ </script>
360
+ </body>
361
  </html>