21 Commits

Author SHA1 Message Date
debec4b609 2eme iteration 2024-10-03 18:20:52 +00:00
fafacb6d3c première iteration du buzzer-manager 2024-10-02 20:34:53 +00:00
20285b945f Merge branch 'main' of https://projects.cloudsucks.net/asco.fablab/BrainBlast 2024-09-11 15:41:02 +02:00
d0cc633264 Ajout d'un draft d'un quizz + un draft de l'exploitation des images via un webrequest sur pictureEngine.js 2024-09-11 15:38:13 +02:00
bfcca3619f recovering sound player 2024-08-05 16:04:01 +00:00
f2aa7e64a0 Changement de l'image dans la solution 2024-04-14 14:50:53 +02:00
9801182db0 Création du groupe de controle des scores dans la zone de boutton 2024-04-14 14:36:22 +02:00
3f078aacb7 Suppression du toggle de thème. Raison ? -> toute l'interface est faite pour fonctionner en version dark, c'est bien plus ergonomique, le logo est également prévu pour être affiché sur une version dark 2024-04-13 16:45:10 +02:00
74f71a0ca3 Refonte de la page de debug MQTT pour y ajouter un UX design, rajout également du timestamp dans le retour de console 2024-04-13 15:49:49 +02:00
08471a27b7 Ajout de deux paramètres dans la page de settings ces boutons permettent de jouer certains sons lors de l'ajout ou la suppression de score 2024-04-12 21:32:08 +02:00
2cc97c8de8 Ajout de la fonctionnalité de réduction de card lorsque l'on clique sur une des trois card dans le gamecontrol 2024-04-12 21:16:12 +02:00
3826a067e2 Décalage du boutton Debugger 2024-04-05 16:03:49 +02:00
76383c1a3d Ajustement du menu router pour cacher le debugger, il est désormais accessible depuis la page paramètre, onglet MQTT 2024-04-05 16:01:13 +02:00
8b012757f3 Ajustement de la section Son pourque les emplacements soient plus optimisés 2024-04-05 15:48:39 +02:00
0dea30e8a6 Modification du titre Brain Blast dans la barre de menu, désormais elle ne s'affiche que lorsque l'on ne se trouve pas sur la page d'accueil (sinon cela fait redondance avec le logo principal) 2024-04-05 15:14:28 +02:00
da4daae323 Suppression de la page About 2024-04-05 15:11:51 +02:00
ef4838bde1 Modification de l'écart de la carte solution par rapport aux autres 2024-04-05 15:03:58 +02:00
1edb73bf5f Création de la carte timer dans le GameStatus 2024-04-05 15:00:41 +02:00
5a983f2c7e Transformation des scores en card 2024-04-05 14:33:54 +02:00
96b7b1cbd7 Ajout de la card Timer pour créer un chronomètre de jeu 2024-04-05 14:29:08 +02:00
080601b792 Ajustement du placement des score, tout est bien aligné et centré 2024-04-05 14:12:22 +02:00
72 changed files with 3755 additions and 765 deletions

217
services/buzzer-manager.js Normal file
View File

@ -0,0 +1,217 @@
// Import necessary modules
const mqtt = require('mqtt');
// MQTT broker configuration
const brokerUrl = 'mqtt://localhost'; // Broker URL (change if needed)
const clientId = 'buzzer_manager';
const options = {
clientId,
clean: true
};
// State variables
let buzzerActive = false; // Indicates if buzzers are blocked
let buzzerThatPressed = null; // Stores the ID of the buzzer that pressed first
let tiltBuzzers = new Set(); // List of buzzer IDs currently in "tilt" mode
// Connect to the MQTT broker
const client = mqtt.connect(brokerUrl, options);
client.on('connect', () => {
console.log(`[INFO] Connected to MQTT broker ${brokerUrl} with ID ${clientId}`);
// Subscribe to topics related to buzzer presses, unlocking, and tilt management
client.subscribe('brainblast/buzzer/pressed/#', (err) => {
if (err) console.error('[ERROR] Failed to subscribe to buzzer presses');
else console.log('[INFO] Successfully subscribed to buzzer presses');
});
client.subscribe('brainblast/buzzer/unlock', (err) => {
if (err) console.error('[ERROR] Failed to subscribe to buzzer unlock');
else console.log('[INFO] Successfully subscribed to buzzer unlock');
});
client.subscribe('brainblast/buzzer/tilt', (err) => {
if (err) console.error('[ERROR] Failed to subscribe to tilt management');
else console.log('[INFO] Successfully subscribed to tilt management');
});
});
// Validate buzzer payload
function validateBuzzerPayload(payload) {
const { buzzer_id, color } = payload;
// Validate buzzer ID (should be a positive integer)
if (typeof buzzer_id !== 'number' || buzzer_id <= 0) {
console.error(`[ERROR] Invalid buzzer ID: ${buzzer_id}`);
return false;
}
// Validate color (should be in the format #RRGGBB)
const validColor = /^#[0-9A-F]{6}$/i.test(color);
if (!validColor) {
console.error(`[ERROR] Invalid color: ${color}`);
return false;
}
return true;
}
// Send updated tilt status
function sendTiltStatus(action, buzzerId) {
const tiltList = Array.from(tiltBuzzers); // Convert Set to Array
client.publish('brainblast/buzzer/status', JSON.stringify({
status: "tilt_update",
tilt_buzzers: tiltList,
message: `Buzzer ID ${buzzerId} ${action} to tilt mode`,
timestamp: new Date().toISOString()
}));
console.log(`[INFO] Tilt status updated: ${tiltList.length} buzzers in tilt mode`);
}
// Handle incoming messages
client.on('message', (topic, message) => {
let payload;
// Parse the incoming message
try {
payload = JSON.parse(message.toString());
} catch (e) {
console.error(`[ERROR] Invalid JSON message received on topic ${topic}: ${message.toString()}`);
return;
}
// Manage tilt mode for buzzers
if (topic === 'brainblast/buzzer/tilt') {
const { buzzer_id, status } = payload;
if (typeof buzzer_id !== 'number' || !['add', 'remove'].includes(status)) {
console.error('[ERROR] Invalid tilt payload, message ignored.');
return;
}
// Update tilt status based on the command
if (status === 'add') {
tiltBuzzers.add(buzzer_id);
console.log(`[INFO] Buzzer ID ${buzzer_id} added to tilt mode`);
} else if (status === 'remove') {
tiltBuzzers.delete(buzzer_id);
console.log(`[INFO] Buzzer ID ${buzzer_id} removed from tilt mode`);
}
// Confirm that the tilt command has been received
client.publish(`brainblast/buzzer/tilt/confirmation/${buzzer_id}`, JSON.stringify({
status: "received",
action: status,
buzzer_id: buzzer_id,
message: `Tilt command '${status}' received for buzzer ID ${buzzer_id}`,
timestamp: new Date().toISOString()
}));
// Send the updated tilt status to all components
sendTiltStatus(status === 'add' ? 'added' : 'removed', buzzer_id);
return;
}
// Detect a buzzer press
if (topic.startsWith('brainblast/buzzer/pressed/')) {
// Validate buzzer payload
if (!validateBuzzerPayload(payload)) {
console.error('[ERROR] Invalid buzzer payload, message ignored.');
return;
}
const buzzerId = payload.buzzer_id; // Unique buzzer ID
const color = payload.color; // Associated hex color
// Always send a confirmation, even if the buzzer is in tilt mode
client.publish(`brainblast/buzzer/confirmation/${buzzerId}`, JSON.stringify({
status: "received",
buzzer_id: buzzerId,
message: `Buzzer ID ${buzzerId} received (Color: ${color})`,
timestamp: new Date().toISOString()
}));
// Ignore if the buzzer is in tilt mode, but notify this event
if (tiltBuzzers.has(buzzerId)) {
console.log(`[INFO] Buzzer ID ${buzzerId} ignored (Tilt mode active)`);
// Notify that the buzzer is in tilt mode and ignored
client.publish(`brainblast/buzzer/tilt/ignored/${buzzerId}`, JSON.stringify({
status: "tilt_ignored",
buzzer_id: buzzerId,
message: `Buzzer ID ${buzzerId} is in tilt mode and ignored.`,
timestamp: new Date().toISOString()
}));
return;
}
// Notify activity even if buzzers are blocked
client.publish('brainblast/buzzer/activity', JSON.stringify({
buzzer_id: buzzerId,
color: color,
status: buzzerActive ? "blocked" : "free",
message: `Activity detected on buzzer ID ${buzzerId} (Color: ${color})`,
timestamp: new Date().toISOString()
}));
if (!buzzerActive) {
// Block further buzzers and record the first pressed ID
buzzerActive = true;
buzzerThatPressed = buzzerId;
console.log(`[INFO] Buzzer activated by ID: ${buzzerId} (Color: ${color})`);
// Notify the light manager to change to the team's color
client.publish('brainblast/light/change', JSON.stringify({
color: color,
effect: 'full_color'
}));
// Notify all components of buzzer blocking
client.publish('brainblast/buzzer/status', JSON.stringify({
status: "blocked",
buzzer_id: buzzerId,
color: color,
message: `Buzzer activated by ID ${buzzerId} (Color: ${color})`,
timestamp: new Date().toISOString()
}));
console.log(`[INFO] Buzzers blocked and notification sent`);
}
}
// Unlock buzzers
if (topic === 'brainblast/buzzer/unlock') {
console.log('[INFO] Buzzer unlock requested');
// Confirm receipt of unlock command
client.publish('brainblast/buzzer/unlock/confirmation', JSON.stringify({
status: "received",
message: "Buzzer unlock command received.",
timestamp: new Date().toISOString()
}));
// Reset buzzer manager state
buzzerActive = false;
buzzerThatPressed = null;
// Notify all components of buzzer unlock
client.publish('brainblast/buzzer/status', JSON.stringify({
status: "unblocked",
message: "Buzzers unblocked and ready for activation.",
timestamp: new Date().toISOString()
}));
console.log('[INFO] Buzzers unblocked and notification sent');
}
});
client.on('error', (err) => {
console.error(`[ERROR] Error connecting to broker: ${err}`);
});
console.log('[INFO] Buzzer manager started...');

497
services/package-lock.json generated Normal file
View File

@ -0,0 +1,497 @@
{
"name": "services",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"mqtt": "^5.10.1"
}
},
"node_modules/@babel/runtime": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
"integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@types/node": {
"version": "22.7.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@types/readable-stream": {
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz",
"integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"safe-buffer": "~5.1.1"
}
},
"node_modules/@types/ws": {
"version": "8.5.12",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
"integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/bl": {
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/bl/-/bl-6.0.16.tgz",
"integrity": "sha512-V/kz+z2Mx5/6qDfRCilmrukUXcXuCoXKg3/3hDvzKKoSUx8CJKudfIoT29XZc3UE9xBvxs5qictiHdprwtteEg==",
"license": "MIT",
"dependencies": {
"@types/readable-stream": "^4.0.0",
"buffer": "^6.0.3",
"inherits": "^2.0.4",
"readable-stream": "^4.2.0"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
"node_modules/commist": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz",
"integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==",
"license": "MIT"
},
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"engines": [
"node >= 6.0"
],
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/concat-stream/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/fast-unique-numbers": {
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz",
"integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.8",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.1.0"
}
},
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/js-sdsl": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mqtt": {
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz",
"integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==",
"license": "MIT",
"dependencies": {
"@types/readable-stream": "^4.0.5",
"@types/ws": "^8.5.9",
"commist": "^3.2.0",
"concat-stream": "^2.0.0",
"debug": "^4.3.4",
"help-me": "^5.0.0",
"lru-cache": "^10.0.1",
"minimist": "^1.2.8",
"mqtt-packet": "^9.0.0",
"number-allocator": "^1.0.14",
"readable-stream": "^4.4.2",
"reinterval": "^1.1.0",
"rfdc": "^1.3.0",
"split2": "^4.2.0",
"worker-timers": "^7.1.4",
"ws": "^8.17.1"
},
"bin": {
"mqtt": "build/bin/mqtt.js",
"mqtt_pub": "build/bin/pub.js",
"mqtt_sub": "build/bin/sub.js"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/mqtt-packet": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz",
"integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==",
"license": "MIT",
"dependencies": {
"bl": "^6.0.8",
"debug": "^4.3.4",
"process-nextick-args": "^2.0.1"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/number-allocator": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz",
"integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.1",
"js-sdsl": "4.3.0"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
},
"node_modules/reinterval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==",
"license": "MIT"
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD"
},
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/worker-timers": {
"version": "7.1.8",
"resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz",
"integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.5",
"tslib": "^2.6.2",
"worker-timers-broker": "^6.1.8",
"worker-timers-worker": "^7.0.71"
}
},
"node_modules/worker-timers-broker": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz",
"integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.5",
"fast-unique-numbers": "^8.0.13",
"tslib": "^2.6.2",
"worker-timers-worker": "^7.0.71"
}
},
"node_modules/worker-timers-worker": {
"version": "7.0.71",
"resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz",
"integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.5",
"tslib": "^2.6.2"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

5
services/package.json Normal file
View File

@ -0,0 +1,5 @@
{
"dependencies": {
"mqtt": "^5.10.1"
}
}

View File

@ -0,0 +1,161 @@
// Import necessary modules
const mqtt = require('mqtt');
// MQTT broker configuration
const brokerUrl = 'mqtt://localhost'; // Broker URL (change if needed)
const options = {
clientId: 'test_buzzer_manager',
clean: true
};
// Set up MQTT client
const client = mqtt.connect(brokerUrl, options);
// Variables for tracking test results
let testResults = {
buzzerActivity: false,
confirmationReceived: false,
statusBlocked: false,
statusUnblocked: false,
tiltAddConfirmed: false,
tiltRemoveConfirmed: false,
tiltIgnored: false,
unlockConfirmation: false,
tiltUpdateAdd: false,
tiltUpdateRemove: false
};
// Subscribe to topics to capture the responses from the buzzer manager
client.on('connect', () => {
console.log('[INFO] Connected to MQTT broker for testing');
// Subscribe to all topics related to the buzzer manager
client.subscribe('brainblast/buzzer/#', (err) => {
if (err) console.error('[ERROR] Failed to subscribe to topics for testing');
else console.log('[INFO] Subscribed to topics successfully');
});
// Run the test sequence after a short delay
setTimeout(runTestSequence, 500);
});
// Capture and process incoming MQTT messages
client.on('message', (topic, message) => {
const payload = JSON.parse(message.toString());
console.log(`[INFO] Message received on ${topic}: ${message.toString()}`);
// Track the test results based on the topics and payloads
if (topic.startsWith('brainblast/buzzer/activity') && payload.buzzer_id === 1) {
testResults.buzzerActivity = true;
}
if (topic.startsWith(`brainblast/buzzer/confirmation/1`) && payload.status === "received") {
testResults.confirmationReceived = true;
}
if (topic === 'brainblast/buzzer/status' && payload.status === "blocked") {
testResults.statusBlocked = true;
}
if (topic === 'brainblast/buzzer/status' && payload.status === "unblocked") {
testResults.statusUnblocked = true;
}
if (topic.startsWith(`brainblast/buzzer/tilt/confirmation/2`) && payload.status === "received" && payload.action === "add") {
testResults.tiltAddConfirmed = true;
}
if (topic.startsWith(`brainblast/buzzer/tilt/confirmation/2`) && payload.status === "received" && payload.action === "remove") {
testResults.tiltRemoveConfirmed = true;
}
if (topic === `brainblast/buzzer/tilt/ignored/2` && payload.status === "tilt_ignored") {
testResults.tiltIgnored = true;
}
if (topic === 'brainblast/buzzer/status' && payload.status === "tilt_update") {
// Check for tilt update with added buzzer
if (payload.tilt_buzzers.includes(2) && payload.message.includes("added")) {
testResults.tiltUpdateAdd = true;
}
// Check for tilt update with removed buzzer
if (!payload.tilt_buzzers.includes(2) && payload.message.includes("removed")) {
testResults.tiltUpdateRemove = true;
}
}
if (topic === 'brainblast/buzzer/unlock/confirmation' && payload.status === "received") {
testResults.unlockConfirmation = true;
}
});
// Function to run the complete test sequence
function runTestSequence() {
console.log('[INFO] Starting test sequence...');
// 1. Simulate a buzzer press (buzzer 1, color red)
console.log('[TEST] Simulating buzzer press (ID 1, color #FF0000)...');
client.publish('brainblast/buzzer/pressed/1', JSON.stringify({
buzzer_id: 1,
color: "#FF0000"
}));
// 2. Simulate a second buzzer press (buzzer 2, color blue) to check blocking
setTimeout(() => {
console.log('[TEST] Simulating second buzzer press (ID 2, color #0000FF)...');
client.publish('brainblast/buzzer/pressed/2', JSON.stringify({
buzzer_id: 2,
color: "#0000FF"
}));
}, 1000);
// 3. Simulate adding a buzzer to tilt mode (buzzer 2)
setTimeout(() => {
console.log('[TEST] Adding buzzer ID 2 to tilt mode...');
client.publish('brainblast/buzzer/tilt', JSON.stringify({
buzzer_id: 2,
status: "add"
}));
}, 1500);
// 4. Simulate pressing a buzzer in tilt mode (should be ignored)
setTimeout(() => {
console.log('[TEST] Simulating tilt buzzer press (ID 2, color #0000FF)...');
client.publish('brainblast/buzzer/pressed/2', JSON.stringify({
buzzer_id: 2,
color: "#0000FF"
}));
}, 2000);
// 5. Remove tilt mode from buzzer 2
setTimeout(() => {
console.log('[TEST] Removing tilt mode for buzzer ID 2...');
client.publish('brainblast/buzzer/tilt', JSON.stringify({
buzzer_id: 2,
status: "remove"
}));
}, 2500);
// 6. Unlock buzzers to reset state
setTimeout(() => {
console.log('[TEST] Unlocking buzzers...');
client.publish('brainblast/buzzer/unlock', '{}');
}, 3000);
// 7. Display results
setTimeout(() => {
console.log('[INFO] Test sequence complete. Results:');
console.log(`1. Buzzer activity detected for buzzer 1: ${testResults.buzzerActivity ? 'PASSED' : 'FAILED'}`);
console.log(`2. Confirmation received for buzzer 1: ${testResults.confirmationReceived ? 'PASSED' : 'FAILED'}`);
console.log(`3. Buzzer 1 status set to "blocked": ${testResults.statusBlocked ? 'PASSED' : 'FAILED'}`);
console.log(`4. Buzzer status set to "unblocked": ${testResults.statusUnblocked ? 'PASSED' : 'FAILED'}`);
console.log(`5. Tilt mode add confirmed for buzzer 2: ${testResults.tiltAddConfirmed ? 'PASSED' : 'FAILED'}`);
console.log(`6. Tilted buzzer press ignored: ${testResults.tiltIgnored ? 'PASSED' : 'FAILED'}`);
console.log(`7. Tilt status update sent (add): ${testResults.tiltUpdateAdd ? 'PASSED' : 'FAILED'}`);
console.log(`8. Tilt mode remove confirmed for buzzer 2: ${testResults.tiltRemoveConfirmed ? 'PASSED' : 'FAILED'}`);
console.log(`9. Tilt status update sent (remove): ${testResults.tiltUpdateRemove ? 'PASSED' : 'FAILED'}`);
console.log(`10. Unlock confirmation received: ${testResults.unlockConfirmation ? 'PASSED' : 'FAILED'}`);
client.end(); // End the MQTT connection
}, 4000);
}

View File

@ -0,0 +1 @@
8-bit-coin-fx_G_minor.wav

View File

@ -0,0 +1 @@
8-bit-coin-fx_G_minor.wav

Binary file not shown.

View File

@ -0,0 +1 @@
mqttBrokerUrl = 'ws://localhost:9001'

482
soundplayer-mqtt/package-lock.json generated Normal file
View File

@ -0,0 +1,482 @@
{
"name": "src",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"mqtt": "^5.3.6",
"play-sound": "^1.1.6"
}
},
"node_modules/@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@types/node": {
"version": "20.11.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.21.tgz",
"integrity": "sha512-/ySDLGscFPNasfqStUuWWPfL78jompfIoVzLJPVVAHBh6rpG68+pI2Gk+fNLeI8/f1yPYL4s46EleVIc20F1Ow==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/readable-stream": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.10.tgz",
"integrity": "sha512-AbUKBjcC8SHmImNi4yK2bbjogQlkFSg7shZCcicxPQapniOlajG8GCc39lvXzCWX4lLRRs7DM3VAeSlqmEVZUA==",
"dependencies": {
"@types/node": "*",
"safe-buffer": "~5.1.1"
}
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bl": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/bl/-/bl-6.0.11.tgz",
"integrity": "sha512-Ok/NWrEA0mlEEbWzckkZVLq6Nv1m2xZ+i9Jq5hZ9Ph/YEcP5dExqls9wUzpluhQRPzdeT8oZNOXAytta6YN8pQ==",
"dependencies": {
"@types/readable-stream": "^4.0.0",
"buffer": "^6.0.3",
"inherits": "^2.0.4",
"readable-stream": "^4.2.0"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/commist": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz",
"integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw=="
},
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"engines": [
"node >= 6.0"
],
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/concat-stream/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/fast-unique-numbers": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.0.tgz",
"integrity": "sha512-lgIjiflW23W7qgagregmo5FFzM+m4/dWaDUVneRi2AV7o2k5npggeEX7srSKlYfJU9fKXvQV2Gzk3272fJT65w==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.2.0"
}
},
"node_modules/find-exec": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/find-exec/-/find-exec-1.0.3.tgz",
"integrity": "sha512-gnG38zW90mS8hm5smNcrBnakPEt+cGJoiMkJwCU0IYnEb0H2NQk0NIljhNW+48oniCriFek/PH6QXbwsJo/qug==",
"dependencies": {
"shell-quote": "^1.8.1"
}
},
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/js-sdsl": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/lru-cache": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
"integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
"engines": {
"node": "14 || >=16.14"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mqtt": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.3.6.tgz",
"integrity": "sha512-3XeyCdHRFf3zZdUUBt/pqprKPtUABc8O4ZGPGs2QPO4sPNTnJels8U2UtBtMt09QCgpUmw8gLTLy2R7verR7kQ==",
"dependencies": {
"@types/readable-stream": "^4.0.5",
"@types/ws": "^8.5.9",
"commist": "^3.2.0",
"concat-stream": "^2.0.0",
"debug": "^4.3.4",
"help-me": "^5.0.0",
"lru-cache": "^10.0.1",
"minimist": "^1.2.8",
"mqtt": "^5.2.0",
"mqtt-packet": "^9.0.0",
"number-allocator": "^1.0.14",
"readable-stream": "^4.4.2",
"reinterval": "^1.1.0",
"rfdc": "^1.3.0",
"split2": "^4.2.0",
"worker-timers": "^7.0.78",
"ws": "^8.14.2"
},
"bin": {
"mqtt": "build/bin/mqtt.js",
"mqtt_pub": "build/bin/pub.js",
"mqtt_sub": "build/bin/sub.js"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/mqtt-packet": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz",
"integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==",
"dependencies": {
"bl": "^6.0.8",
"debug": "^4.3.4",
"process-nextick-args": "^2.0.1"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/number-allocator": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz",
"integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==",
"dependencies": {
"debug": "^4.3.1",
"js-sdsl": "4.3.0"
}
},
"node_modules/play-sound": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/play-sound/-/play-sound-1.1.6.tgz",
"integrity": "sha512-09eO4QiXNFXJffJaOW5P6x6F5RLihpLUkXttvUZeWml0fU6x6Zp7AjG9zaeMpgH2ZNvq4GR1ytB22ddYcqJIZA==",
"dependencies": {
"find-exec": "1.0.3"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/reinterval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="
},
"node_modules/rfdc": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
"integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg=="
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/shell-quote": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/worker-timers": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.2.tgz",
"integrity": "sha512-iqhXt5+Mc3u2nHj3G/w/E9pXqhlueniA2NlyelB/MQSHQuuW2fmmZGkveAv6yi4SSZvrpbveBBlqPSZ0MDCLww==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"tslib": "^2.6.2",
"worker-timers-broker": "^6.1.2",
"worker-timers-worker": "^7.0.66"
}
},
"node_modules/worker-timers-broker": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.2.tgz",
"integrity": "sha512-slFupigW5vtkGJ1VBCxYPwXFFRmvfioh02bCltBhbMkt3fFnkAbKBCg61pNTetlD0RAsP09mqx/FB0f4UMoHNw==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"fast-unique-numbers": "^9.0.0",
"tslib": "^2.6.2",
"worker-timers-worker": "^7.0.66"
}
},
"node_modules/worker-timers-worker": {
"version": "7.0.66",
"resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.66.tgz",
"integrity": "sha512-VCLa0H5K9fE2DVI/9r5zDuFrMQIpNL3UD/h4Ui49fIiRBTgv1Sqe0RM12brr83anBsm103aUQkvKvCBL+KpNtg==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"tslib": "^2.6.2"
}
},
"node_modules/ws": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

View File

@ -0,0 +1,6 @@
{
"dependencies": {
"mqtt": "^5.3.6",
"play-sound": "^1.1.6"
}
}

View File

@ -0,0 +1,54 @@
const config = require('./config')
const player = require('play-sound')();
const mqtt = require('mqtt')
// Créer une instance de client MQTT
const client = mqtt.connect(config.mqttBrokerUrl);
const messages_sounds_maps = {
'success': "./assets/sounds/success.mp3",
'fail': "./assets/sounds/error.mp3",
'timer': "./assets/sounds/timer.mp3",
'bell': "/home/lol/Src/fablab/brain-blast-services/src/assets/sounds/coin.wav",
'applause': "./assets/sound/clap.mp3"
}
// Gérer les événements de connexion
client.on('connect', function () {
console.log('Connecté au broker MQTT')
// S'abonner à un topic
client.subscribe('/sound/playsound', function (err) {
if (err) {
console.error('Erreur lors de la souscription au topic', err)
} else {
console.log('Souscription au topic réussie')
}
});
});
// Gérer les messages entrants
client.on('message', function (topic, message) {
let obj = JSON.parse(message)
const audioFile = messages_sounds_maps[obj]
console.log('Message reçu sur le topic', topic, ':', obj)
console.log('Je vais lire le fichier : ', audioFile)
if (audioFile) {
// Jouer le fichier audio correspondant au message reçu
player.play(audioFile, function(err){
if (err) {
console.error('Erreur lors de la lecture du fichier audio', err);
} else {
console.log('Fichier audio lu avec succès');
}
});
} else {
console.warn('Aucun fichier audio correspondant au message reçu');
}
});
// Gérer les erreurs de connexion
client.on('error', function (error) {
console.error('Erreur de connexion au broker MQTT', error);
});

View File

@ -1,13 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Brain Blast</title>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Blast</title>
</head> </head>
<body> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,8 +1,5 @@
{ {
"compilerOptions": { "compilerOptions": { "paths": { "@/*": ["./src/*"] }
"paths": {
"@/*": ["./src/*"]
}
}, },
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }

1497
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
{ {
"name": "brain-blast", "name": "brain-blast",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": false,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev-complete": "concurrently \"vite\" \"node ./src/services/buzzerWatcher.js\"",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
@ -12,15 +13,19 @@
}, },
"dependencies": { "dependencies": {
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",
"express": "^5.0.0",
"mqtt": "^5.3.5", "mqtt": "^5.3.5",
"ping": "^0.4.4",
"roboto-fontface": "^0.10.0", "roboto-fontface": "^0.10.0",
"vue": "^3.4.19", "vue": "^3.4.19",
"vue-router": "^4.2.5" "vue-router": "^4.2.5",
"vuex": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",
"concurrently": "^8.2.2",
"eslint": "^8.49.0", "eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",

View File

@ -1,11 +1,11 @@
<template> <template>
<v-app> <v-app>
<BrainBlastBar /> <BrainBlastBar />
<GameStatus v-if="$route.name === 'Game Control (Présentateur)'"></GameStatus> <GameStatus v-if="$route.name === 'Game Control (Présentateur)'">
</GameStatus>
<v-main> <v-main>
<RouterView /> <RouterView />
</v-main> </v-main> <!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
<!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
</v-app> </v-app>
</template> </template>

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,27 +1,10 @@
<template> <template>
<v-app-bar :elevation="5" height="50"> <v-app-bar :elevation="5" height="50">
<RouterMenu /> <RouterMenu />
<v-app-bar-title>Brain Blast</v-app-bar-title> <v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title>
<template v-slot:append>
<v-btn icon @click="toggleTheme">
<v-icon>{{ darkTheme ? 'mdi-white-balance-sunny' : 'mdi-moon-waning-crescent' }}</v-icon>
</v-btn>
</template>
</v-app-bar> </v-app-bar>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'
import { useTheme } from 'vuetify'
import RouterMenu from '@/components/RouterMenu.vue' import RouterMenu from '@/components/RouterMenu.vue'
const theme = useTheme()
const darkTheme = ref(true)
function toggleTheme() {
darkTheme.value = !darkTheme.value
theme.global.name.value = theme.global.current.value.dark ? 'CustomThemeLight' : 'CustomThemeDark'
}
</script> </script>

View File

@ -0,0 +1,114 @@
<template>
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-calculator-variant</v-icon>
Gestion des scores
</v-card-title>
<v-container class="text-center">
<v-row justify="center">
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn red xs12 sm6 md3" topic="/game/score" message="TEAM_RED-2"rounded>
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="/game/score" message="TEAM_RED-1">
<v-icon left size="40">mdi-minus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="/game/score" message="TEAM_RED+1">
<v-icon left size="40">mdi-plus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3 " topic="/game/score" message="TEAM_RED+2">
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE-2">
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE-1">
<v-icon left size="40">mdi-minus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE+1">
<v-icon left size="40">mdi-plus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE+2">
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE-2">
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE-1">
<v-icon left size="40">mdi-minus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE+1">
<v-icon left size="40">mdi-plus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE+2">
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN-2">
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN-1">
<v-icon left size="40">mdi-minus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="4" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN+1">
<v-icon left size="40">mdi-plus-box</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="3">
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN+2">
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script setup>
import MqttButton from './MqttButton.vue';
import { ref } from 'vue';
// Variable pour contrôler l'état de la carte
const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte
function toggleCardSize() {
isCardReduced.value = !isCardReduced.value;
}
</script>
<style>
.card--reduced {
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
width: 170px;
overflow: hidden;
transition: height 0.3s ease-in-out;
}
</style>

View File

@ -1,36 +1,34 @@
<template> <template>
<v-card tile outlined class="card"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon>
Contrôles Contrôle du jeu
</v-card-title> </v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="5" class="mt-4"> <v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="220" height="110" class="btn xs12 sm6 md3" topic="/display/control" message="previous"> <mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous">
<v-icon left size="80">mdi-skip-previous</v-icon> <v-icon left size="60">mdi-skip-previous</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="5" class="mt-4"> <v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3" topic="/display/control" message="next"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="next"> <v-icon left size="60">mdi-skip-next</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="pause"> <v-icon left size="60">mdi-pause</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="play"> <v-icon left size="60">mdi-play</v-icon> </mqtt-button> </v-col> </v-row> </v-container>
<v-icon left size="80">mdi-skip-next</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3" topic="/display/control" message="pause">
<v-icon left size="80">mdi-pause</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3 " topic="/display/control" message="play">
<v-icon left size="80">mdi-play</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card> </v-card>
</template> </template>
<script setup> <script setup>
import MqttButton from './MqttButton.vue'; import MqttButton from './MqttButton.vue';
import { ref } from 'vue';
// Variable pour contrôler l'état de la carte
const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte
function toggleCardSize() { isCardReduced.value = !isCardReduced.value;
}
</script> </script>
<style>
.card--reduced { height: 56px; /* Réglez la hauteur réduite selon vos besoins */ width: 170px; overflow: hidden; transition: height 0.3s ease-in-out;
}
</style>

View File

@ -0,0 +1,45 @@
<template> <div class="label-pos"> <v-label class="labelTitle-style pb-4">Scores</v-label> </div>
<!-- Équipes Rouges et Bleues côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Rouge --> <v-col cols="6"> <!-- Colonnes de taille 6 pour chaque équipe --> <v-row no-gutters> <v-col class="scorediv-style-red"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ RedRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ RedTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
<!-- Équipe Bleue --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-blue"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ BlueRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ BlueTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
<!-- Équipes Oranges et Vertes côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Orange --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-orange"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ OrangeRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ OrangeTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
<!-- Équipe Verte --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-green"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ GreenRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ GreenTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
</template>
<script setup>
import { ref } from 'vue'; // Import des fonctions de Vue 3
import variables from '@/variables.js';
// Déclaration des variables locales pour les scores
const RedTotalScore = ref(variables.RedTotalScore);
const BlueTotalScore = ref(variables.BlueTotalScore);
const OrangeTotalScore = ref(variables.OrangeTotalScore);
const GreenTotalScore = ref(variables.GreenTotalScore);
const RedRoundScore = ref(variables.RedRoundScore);
const BlueRoundScore = ref(variables.BlueRoundScore);
const OrangeRoundScore = ref(variables.OrangeRoundScore);
const GreenRoundScore = ref(variables.GreenRoundScore);
</script>
<style>
.label-pos { padding-top: 15px; text-align: center;
}
.labelTitle-style { font-size: 20px !important; font-weight: 500; color: #d42828 !important; opacity: 90% !important;
}
.labelRoundScore-style { opacity: 100% !important; font-size: 25px !important; font-weight: 500;
}
.labelTotalScore-style { opacity: 100% !important; font-size: 15px !important; font-weight: 500;
}
.button-pos { padding-top: 10px; padding-bottom: 15px;
}
.scorebox-pos { text-align: center;
}
.scorediv-style-red { background-color: #d42828 !important; padding: 15px; border-top-left-radius: 10%;
}
.scorediv-style-orange { background-color: #d48f28 !important; padding: 15px; border-bottom-left-radius: 10%;
}
.scorediv-style-blue { background-color: #2867d4 !important; padding: 15px; border-top-right-radius: 10%;
}
.scorediv-style-green { background-color: #28d42e !important; padding: 15px; border-bottom-right-radius: 10%;
}
</style>

View File

@ -1,14 +1,13 @@
<template> <template>
<v-card tile outlined class="card"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title feedback"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon>
Solution Solution </v-card-title>
</v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<v-row justify="center"> <v-row justify="center">
<v-container class="text-center"> <v-container class="text-center"> <!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
<!-- Utilisation de styles CSS personnalisés pour centrer l'image --> <v-img width="450" src="@/assets/copilot-solution-FULL-HD.jpg" style="margin: 0 auto;">
<v-img width="400" src="https://c4.wallpaperflare.com/wallpaper/908/893/291/funny-middle-finger-black-background-wallpaper-preview.jpg" style="margin: 0 auto;"></v-img> </v-img>
</v-container> </v-container>
</v-row> </v-row>
</v-container> </v-container>
@ -17,8 +16,7 @@
<style> <style>
@media (min-width: 1024px) { @media (min-width: 1024px) {
.image-container { .image-container {
width: 300px; width: 300px; overflow: hidden; /* Pour masquer le dépassement de l'image */
overflow: hidden; /* Pour masquer le dépassement de l'image */
border: 1px solid #ccc; /* Bordure de l'image */ border: 1px solid #ccc; /* Bordure de l'image */
} }
.image-container img { .image-container img {
@ -26,5 +24,23 @@
height: auto; /* Pour maintenir le ratio d'aspect de l'image */ height: auto; /* Pour maintenir le ratio d'aspect de l'image */
display: block; /* Pour éviter l'espace réservé pour les images */ display: block; /* Pour éviter l'espace réservé pour les images */
} }
} }
.card--reduced {
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
width: 160px;
overflow: hidden;
transition: height 0.6s ease-in-out;
}
</style> </style>
<script setup>
import { ref } from 'vue';
// Variable pour contrôler l'état de la carte
const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte
function toggleCardSize() {
isCardReduced.value = !isCardReduced.value;
}
</script>

View File

@ -1,36 +1,36 @@
<template> <template>
<v-card tile outlined class="card"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon>
Soundboard Soundboard
</v-card-title> </v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="good-response" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded>
<v-icon size="70">mdi-check-circle-outline</v-icon> <v-icon size="60">mdi-check-circle-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="bad-response" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded>
<v-icon size="70">mdi-close-circle-outline</v-icon> <v-icon size="60">mdi-close-circle-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="timer" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded>
<v-icon size="70">mdi-timer-outline</v-icon> <v-icon size="60">mdi-timer-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
</v-row> </v-row>
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mb-4"> <v-col cols="12" sm="6" md="4" class="mb-4">
<mqtt-button class="btn" width="220" height="110" topic="/sound/playsound" message="applause" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded>
<v-icon size="70">mdi-human-handsup</v-icon> <v-icon size="60">mdi-human-handsup</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mb-4"> <v-col cols="12" sm="6" md="4" class="mb-4">
<mqtt-button class="btn" width="220" height="110" topic="/sound/playsound" message="bell" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded>
<v-icon size="70">mdi-bell-outline</v-icon> <v-icon size="60">mdi-bell-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
</v-row> </v-row>
@ -40,4 +40,22 @@
<script setup> <script setup>
import MqttButton from './MqttButton.vue'; import MqttButton from './MqttButton.vue';
import { ref } from 'vue';
// Variable pour contrôler l'état de la carte
const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte
function toggleCardSize() {
isCardReduced.value = !isCardReduced.value;
}
</script> </script>
<style scoped>
.card--reduced {
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
width: 190px;
overflow: hidden;
transition: height 0.3s ease-in-out;
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<div class="container">
<div class="timer">
<v-label color="primary" class="labelTime-style" >{{ formatTime }}</v-label>
</div>
<v-row no-gutters justify="space-around" >
<v-btn class="buttons" color="primary" icon="mdi-play" @click="startTimer"></v-btn>
<v-btn color="primary" icon="mdi-pause" @click="pauseTimer"></v-btn>
<v-btn color="primary" icon="mdi-restart" @click="resetTimer"></v-btn>
</v-row>
</div>
</template>
<script setup>
import { ref, computed, onBeforeUnmount } from 'vue';
const timerActive = ref(false);
const startTime = ref(null);
const currentTime = ref(null);
const elapsedTime = ref(0);
const formatTime = computed(() => {
let seconds = Math.floor(elapsedTime.value / 1000);
let minutes = Math.floor(seconds / 60);
let hours = Math.floor(minutes / 60);
seconds = seconds % 60; minutes = minutes % 60;
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
});
const pad = (number) => {
return (number < 10 ? "0" : "") + number;
};
const startTimer = () => {
if (!timerActive.value) {
timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer(); }
};
const pauseTimer = () => {
if (timerActive.value) {
timerActive.value = false;
clearInterval(currentTime.value); }
};
const resetTimer = () => {
elapsedTime.value = 0;
timerActive.value = false;
clearInterval(currentTime.value);
};
const updateTimer = () => {
currentTime.value = setInterval(() => {elapsedTime.value = Date.now() - startTime.value; }, 1000);
};
onBeforeUnmount(() => { clearInterval(currentTime.value);
});
</script>
<script>
const startTimer = () => {
if (!timerActive.value) {
timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer(); } };
const pauseTimer = () => {
if (timerActive.value) {
timerActive.value = false;
clearInterval(currentTime.value); } };
const resetTimer = () => {
elapsedTime.value = 0;
timerActive.value = false;
clearInterval(currentTime.value); };
export { startTimer, pauseTimer, resetTimer };
</script>
<style>
.container {
text-align: center;
margin-top: auto; /* Place le container en bas de son parent */
margin-bottom: 1px; /* Marge en bas pour un espacement */
position: fixed; /* Le positionne de manière fixe */
left: 0;
right: 0;
bottom: 0;
padding: 20px;
}
.timer {
margin-bottom: 15px;
}
.labelTime-style {
font-size: 30px !important;
font-weight: 500;
color: #d42828 !important;
opacity: 90% !important;
}
.buttons{
background-color: rgb(255, 255, 255);
}
</style>

View File

@ -1,171 +1,41 @@
<template> <template>
<v-navigation-drawer width="250"> <v-navigation-drawer width="250"> <!-- Augmenter la largeur pour deux équipes côte à côte -->
<div class="label-pos"> <div class="label-pos">
<v-label class="labelTitle-style">Buzzer connectés</v-label> <v-label class="labelTitle-style">Buzzer connectés</v-label>
</div> </div>
<v-row no-gutters justify="space-around" class="button-pos"> <v-row no-gutters justify="space-around" class="button-pos">
<v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon>
</v-row> </v-row>
<v-divider :thickness="2" class="border-opacity-100" color="primary"/> <v-divider :thickness="2" class="border-opacity-100" color="primary"/>
<div class="label-pos"> <CardScore/>
<v-label class="labelTitle-style pb-7">Scores</v-label> <CardTimer/>
</div>
<div>
<v-row no-gutters justify="space-around" class="scorebox-1-pos">
<v-row>
<v-col class="align-start scorediv-style-red pl-1">
<v-col>
<div class="pr-1">
<div>
<v-label class="labelRoundScore-style">Manche</v-label>
</div>
<v-label class="labelRoundScore-style">{{ RedRoundScore }}</v-label>
</div>
<v-divider class="pr-1" color="background"/>
</v-col>
<div class="pr-1">
<div>
<v-label class="labelTotalScore-style">Total</v-label>
</div>
<v-label class="labelTotalScore-style">{{ RedTotalScore }}</v-label>
</div>
</v-col>
<v-col class="align-start scorediv-style-blue pl-1">
<v-col>
<div class="pr-1">
<div>
<v-label class="labelRoundScore-style">Manche</v-label>
</div>
<v-label class="labelRoundScore-style">{{ BlueRoundScore }}</v-label>
</div>
<v-divider class="pr-1" color="background"/>
</v-col>
<div class="pr-1">
<div>
<v-label class="labelTotalScore-style">Total</v-label>
</div>
<v-label class="labelTotalScore-style">{{ BlueTotalScore }}</v-label>
</div>
</v-col>
</v-row>
</v-row>
</div>
<div>
<v-row no-gutters justify="space-around" class="scorebox-2-pos mb-0">
<v-row>
<v-col class="scorediv-style-orange pl-1">
<v-col>
<div class="pr-1">
<div>
<v-label class="labelRoundScore-style">Manche</v-label>
</div>
<v-label class="labelRoundScore-style">{{ OrangeRoundScore }}</v-label>
</div>
<v-divider class="pr-1" color="background"/>
</v-col>
<div class="pr-1">
<div>
<v-label class="labelTotalScore-style">Total</v-label>
</div>
<v-label class="labelTotalScore-style">{{ OrangeTotalScore }}</v-label>
</div>
</v-col>
<v-col class="align-start scorediv-style-green pl-1">
<v-col>
<div class="pr-1">
<div>
<v-label class="labelRoundScore-style">Manche</v-label>
</div>
<v-label class="labelRoundScore-style">{{ GreenRoundScore }}</v-label>
</div>
<v-divider class="pr-1" color="background"/>
</v-col>
<div class="pr-1">
<div>
<v-label class="labelTotalScore-style">Total</v-label>
</div>
<v-label class="labelTotalScore-style">{{ GreenTotalScore }}</v-label>
</div>
</v-col>
</v-row>
</v-row>
</div>
</v-navigation-drawer> </v-navigation-drawer>
</template> </template>
<script setup> <script setup>
import { ref} from 'vue'; // Import des fonctions de Vue 3 import CardTimer from '@/components/CardTimer.vue'
import variables from '@/variables.js'; import CardScore from '@/components/CardScore.vue'
import { ref } from 'vue'; // Import des fonctions de Vue 3
// Déclaration des variables locales pour les scores import variables from '@/variables.js';
const RedTotalScore = ref(variables.RedTotalScore);
const BlueTotalScore = ref(variables.BlueTotalScore);
const OrangeTotalScore = ref(variables.OrangeTotalScore);
const GreenTotalScore = ref(variables.GreenTotalScore);
const RedRoundScore = ref(variables.RedRoundScore);
const BlueRoundScore = ref(variables.BlueRoundScore);
const OrangeRoundScore = ref(variables.OrangeRoundScore);
const GreenRoundScore = ref(variables.GreenRoundScore);
</script> </script>
<style> <style>
.label-pos{ .label-pos {
padding-top: 15px; padding-top: 15px;
text-align: center; text-align: center;
} }
.labelTitle-style{ .labelTitle-style {
font-size: 20px !important; font-size: 20px !important;
font-weight: 500; font-weight: 500;
color: #e91e1e !important; color: #e91e1e !important;
opacity: 90% !important; opacity: 90% !important;
} }
.labelRoundScore-style{ .button-pos {
opacity: 100% !important;
font-size: 25px !important;
font-weight: 500;
}
.labelTotalScore-style{
opacity: 100% !important;
font-size: 15px !important;
font-weight: 500;
}
.button-pos{
padding-top: 10px; padding-top: 10px;
padding-bottom: 15px; padding-bottom: 15px;
} }
.scorebox-1-pos{
padding-bottom: 15px;
text-align: center;
}
.scorebox-2-pos{
padding-top: 9px;
text-align: center;
}
.scorediv-style-red{
background-color: #d42828 !important;
}
.scorediv-style-orange{
background-color: #d48f28 !important;
}
.scorediv-style-blue{
background-color: #2867d4 !important;
}
.scorediv-style-green{
background-color: #28d42e !important;
}
</style> </style>

View File

@ -1,23 +1,58 @@
<template> <template>
<div> <v-container class="v-container-style">
<h1>Console MQTT</h1> <v-card tile outlined width="500">
<div v-for="(message, index) in messages" :key="index">{{ message }}</div> <v-card-title class="card__title primary centered-title">
<v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon>
Console MQTT
</v-card-title>
<v-container class="text-center">
<div v-for="(log, index) in messageLogs" :key="index">
<v-label class="v-label-timestamp">{{ log.timestamp }} </v-label> -
<v-label>{{ log.message }}</v-label>
</div> </div>
</v-container>
</v-card>
</v-container>
</template> </template>
<script> <script>
import { subscribeToTopic } from '@/services/mqttService' import { subscribeToTopic } from '@/services/mqttService'
export default { export default {
data() { data() { return {
return { messageLogs: [] // Initialiser un tableau pour stocker les messages MQTT avec horodatage
messages: [] // Initialiser un tableau pour stocker les messages MQTT
} }
}, },
created() { created() { subscribeToTopic('#', (topic, message) => { // Obtenir l'horodatage actuel
subscribeToTopic('#', (topic, message) => { const timestamp = new Date().toLocaleString('fr-FR', {
this.messages.push(`Topic: ${topic}, Message: ${message}`) // Ajouter le message à la liste des messages hour12: false,
}) // S'abonner à tous les topics MQTT hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// Ajouter le message avec l'horodatage à la liste des messages
this.messageLogs.push({ timestamp, message: `Topic: ${topic}, Message: ${message}` });
// Limiter la liste à 10 messages
if (this.messageLogs.length > 10) {
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
} }) // S'abonner à tous les topics MQTT
}
} }
}
</script> </script>
<style>
.v-container-style {
display: flex;
justify-content: center;
}
.centered-title {
text-align: center;
}
.v-label-timestamp{
opacity: 100%;
font-weight: 700;
color: #d42828;
}
</style>

View File

@ -1,40 +1,52 @@
<template> <template>
<div> <v-container class="v-container-style">
<h1>Publier sur MQTT</h1> <v-card tile outlined width="500">
<select v-model="selectedTopic"> <v-card-title class="card__title primary centered-title">
<option v-for="topic in topics" :key="topic">{{ topic }}</option> <v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
</select> Publier un message
<input type="text" v-model="message" placeholder="Saisissez votre message" /> </v-card-title>
<button @click="publishMessage">Publier sur MQTT</button> <div class="input-style">
</div> <v-select label="Topic" v-model="selectedTopic" :items="topics" prepend-icon="mdi-target"></v-select>
<v-text-field label="Message" v-model="message" prepend-icon="mdi-text-box"></v-text-field>
</div> <v-btn class="v-btn-style" height="50" @click="publishMessage">Publier</v-btn>
</v-card>
</v-container>
</template> </template>
<script> <script>
import { publishMessage } from '@/services/mqttService' import { publishMessage } from '@/services/mqttService'
export default { export default {
data() { data() { return { message: '', // Initialiser la variable message
return {
message: '', // Initialiser la variable message
selectedTopic: 'topic1', selectedTopic: 'topic1',
topics: [ topics: [ '/display/control', '/sound/playsound', 'topic3', 'topic4', 'topic5', 'topic6', 'topic7', 'topic8', 'topic9', 'topic10' ] // Liste des topics
'topic1',
'topic2',
'topic3',
'topic4',
'topic5',
'topic6',
'topic7',
'topic8',
'topic9',
'topic10'
] // Liste des topics
} }
}, },
methods: { methods: {
publishMessage() { publishMessage() {
publishMessage(this.selectedTopic, this.message) publishMessage(this.selectedTopic, this.message) }
}
} }
} }
</script> </script>
<style>
.v-container-style {
align-items: center;
justify-content: center;
}
.input-style{
margin: 20px;
}
.v-btn-style{
align-items: center;
justify-content: center;
width: 100%;
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
border-top-right-radius: 0%;
border-top-left-radius: 0%;
}
.centered-title {
text-align: center;
}
</style>

View File

@ -2,23 +2,28 @@
<v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon> <v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon>
<v-menu v-model="menu" class="menu-below-bar"> <v-menu v-model="menu" class="menu-below-bar">
<v-list> <v-list>
<v-list-item v-for="route in routes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item> <v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, computed } from 'vue';
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router';
const router = useRouter() const router = useRouter();
const routes = router.options.routes const routes = router.options.routes;
let menu = ref(false) let menu = ref(false);
// Filtrer les routes pour masquer une route spécifique (par exemple, 'RouteA')
const filteredRoutes = computed(() => {
return routes.filter(route => route.name !== 'Debugger MQTT');
});
</script> </script>
<style scoped> <style scoped>
.menu-below-bar { .menu-below-bar {
margin-top: 48px; /* La hauteur de la barre d'application */ margin-top: 48px; /* La hauteur de la barre d'application */
} }
</style> </style>

View File

@ -4,5 +4,13 @@
// config.js // config.js
export default { export default {
mqttBrokerUrl: 'ws://localhost:9001' mqttBrokerUrl: 'ws://192.168.1.78:9001',
// Buzzer
redBuzzerIP: '',
blueBuzzerIP: '',
orangeBuzzerIP: '',
greenBuzzerIP: ''
// Light
}; };

View File

@ -3,20 +3,11 @@ import HomeView from '../views/HomeView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [ {
{
path: '/', path: '/',
name: 'Home', name: 'Accueil',
component: HomeView component: HomeView
}, },
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/views/AboutView.vue')
},
{ {
path: '/game/control', path: '/game/control',
name: 'Game Control (Présentateur)', name: 'Game Control (Présentateur)',
@ -35,8 +26,7 @@ const router = createRouter({
{ {
path: '/settings', path: '/settings',
name: 'Paramètres', name: 'Paramètres',
component: () => import('@/views/SettingsView.vue') component: () => import('@/views/SettingsView.vue') }
}
] ]
}) })

View File

@ -15,7 +15,7 @@ const CustomThemeDark = {
dark: true, dark: true,
colors: { colors: {
background: '#121212', background: '#121212',
primary: '#e91e1e', primary: '#d42828',
secondary: '#F44336', secondary: '#F44336',
accent: '#FFC107', accent: '#FFC107',
error: '#e91e1e', error: '#e91e1e',
@ -32,13 +32,17 @@ const CustomThemeLight = {
dark: false, dark: false,
colors: { colors: {
background: '#ffffff', background: '#ffffff',
primary: '#e91e1e', primary: '#d42828',
secondary: '#F44336', secondary: '#F44336',
accent: '#FFC107', accent: '#FFC107',
error: '#e91e1e', error: '#e91e1e',
warning: '#FFC107', warning: '#FFC107',
info: '#607D8B', info: '#607D8B',
success: '#4CAF50' success: '#4CAF50',
BuzzerBlue: '#2867d4',
BuzzerOrange: '#d48f28',
BuzzerRed: '#d42828',
BuzzerGreen: '#28d42e',
} }
} }
@ -48,7 +52,6 @@ export default createVuetify({
defaultTheme: 'CustomThemeDark', defaultTheme: 'CustomThemeDark',
themes: { themes: {
CustomThemeDark, CustomThemeDark,
CustomThemeLight, CustomThemeLight, },
},
}, },
}) })

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -0,0 +1,27 @@
name: "Histoire & Géographie"
questions:
1:
Q: "Quelle bataille célèbre s'est déroulée en 1815, marquant la défaite de Napoléon Bonaparte ?"
T: "Elle a eu lieu en Belgique"
R: "La bataille de Waterloo."
P: "Q-1.jpeg"
2:
Q: "Quelle est la capitale de l'Australie ?"
T: "Le nom de cette ville commence par la lettre 'C'"
R: "Canberra."
P: "Q-2.jpeg"
3:
Q: "En quelle année la Seconde Guerre mondiale a-t-elle pris fin ?"
T: "C'est au milieu des années 40."
R: "En 1945."
P: "Q-3.jpeg"
4:
Q: "Quel fleuve traverse la ville du Caire en Égypte ?"
T: "C'est l'un des plus longs fleuves du monde"
R: "Le Nil."
P: "Q-4.jpeg"
5:
Q: "Quel pays a été divisé par un mur de 1961 à 1989 ?"
T: "Sa chute a marqué la fin de la guerre froide."
R: "L'Allemagne (le mur de Berlin)."
P: "Q-5.jpeg"

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

View File

View File

View File

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -0,0 +1,27 @@
name: "Jeux vidéos"
questions:
1:
Q: "Quel personnage de jeu vidéo est un plombier moustachu qui saute sur des ennemis pour sauver une princesse ?"
T: "Cest le personnage le plus célèbre de Nintendo, son nom commence par 'M'."
R: "Mario."
P: "Q-1.jpeg"
2:
Q: "Quel jeu vidéo multijoueur de football avec des voitures est très populaire ?"
T: "Il s'agit d'un mélange de sport et de voitures rapides."
R: "Rocket League."
P: "Q-2.jpeg"
3:
Q: "Quel jeu vidéo mobile consiste à faire exploser des bonbons en alignant trois pièces identiques ?"
T: "Son nom fait référence aux bonbons."
R: "Candy Crush Saga."
P: "Q-3.jpeg"
4:
Q: "Quel est le nom du célèbre personnage bleu de SEGA qui court à une vitesse incroyable ?"
T: "Son nom commence par la lettre 'S' et c'est un hérisson."
R: "Sonic"
P: "Q-4.jpeg"
5:
Q: "Quel jeu permet de construire et explorer un monde fait de blocs, tout en survivant face à des monstres ?"
T: "Le monde est entièrement fait de blocs carrés."
R: "Minecraft."
P: "Q-5.jpeg"

View File

@ -0,0 +1,64 @@
// index.js
import Vue from 'vue';
import Vuex from 'vuex';
import ping from 'ping';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
buzzerRedState: '',
buzzerBlueState: '',
buzzerOrangeState: '',
buzzerGreenState: ''
},
mutations: {
setBuzzerRedState(state, newState) {
state.buzzerRedState = newState;
},
setBuzzerBlueState(state, newState) {
state.buzzerBlueState = newState;
},
setBuzzerOrangeState(state, newState) {
state.buzzerOrangeState = newState;
},
setBuzzerGreenState(state, newState) {
state.buzzerGreenState = newState;
}
},
actions: {
updateBuzzerStates({ commit }, { index, newState }) {
switch (index) {
case 0:
commit('setBuzzerRedState', newState);
break;
case 1:
commit('setBuzzerBlueState', newState);
break;
case 2:
commit('setBuzzerOrangeState', newState);
break;
case 3:
commit('setBuzzerGreenState', newState);
break;
default:
console.error('Index de buzzer invalide:', index);
}
}
}
});
const hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'];
async function pingHosts() {
hosts.forEach(async (host, index) => { try {
const res = await ping.promise.probe(host);
store.dispatch('updateBuzzerStates', {index, newState: res.alive ? 'ON' : 'OFF' }); }
catch (error) {
console.error(`Erreur lors du ping de ${host}:`, error.message); }
});
}
pingHosts();
module.exports = store;

View File

@ -13,7 +13,6 @@ export function publishMessage(topic, message) {
// Fonction pour s'abonner à un topic MQTT et écouter les messages entrants // Fonction pour s'abonner à un topic MQTT et écouter les messages entrants
export function subscribeToTopic(topic, callback) { export function subscribeToTopic(topic, callback) {
client.subscribe(topic) client.subscribe(topic)
client.on('message', (receivedTopic, message) => { client.on('message', (receivedTopic, message) => { callback(receivedTopic.toString(), message.toString())
callback(receivedTopic.toString(), message.toString())
}) })
} }

View File

@ -0,0 +1,43 @@
import express from 'express';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const port = 3000;
// Obtenir le chemin du répertoire parent
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Middleware pour gérer les requêtes depuis le frontend
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
next();
});
// Le dossier assets est situé un niveau au-dessus du dossier services
const assetsDir = path.join(__dirname, '..', 'quizz/geography-history');
console.log(assetsDir)
// Middleware pour servir les fichiers statiques
app.use('/images', express.static(assetsDir));
// API pour lister les fichiers d'image
app.get('/images-list', async (req, res) => {
try {
const files = await fs.readdir(assetsDir);
// Filtrer pour ne renvoyer que les fichiers d'image (par ex : .jpg, .png)
const images = files.filter(file => /\.(jpg|jpeg|png|gif)$/.test(file));
res.json(images);
} catch (err) {
res.status(500).send('Erreur lors de la lecture du dossier');
}
});
app.listen(port, () => {
console.log(`Serveur démarré sur http://localhost:${port}`);
});

View File

@ -23,10 +23,5 @@ export default {
}; };
// Variables localStorage // Variables localStorage
export const localStorageVars = { export const localStorageVars = { // Exemple de variable localStorage RedScorelocal: localStorage.getItem('RedScore') || '', BlueScorelocal: localStorage.getItem('BlueScore') || '', OrangeScorelocal: localStorage.getItem('OrangeScore') || '', GreenScorelocal: localStorage.getItem('GreenScore') || '',
// Exemple de variable localStorage
RedScorelocal: localStorage.getItem('RedScore') || '',
BlueScorelocal: localStorage.getItem('BlueScore') || '',
OrangeScorelocal: localStorage.getItem('OrangeScore') || '',
GreenScorelocal: localStorage.getItem('GreenScore') || '',
}; };

View File

@ -1,24 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
<hr/>
<p>
Exemple d'une page
</p><p>
Ou il y a un scroll
</p><p>
Pour mettre en valeur leu footer fixe en bas de page
</p><p>
Cette page n'a aucun interet et pourra etre supprimée
</p>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
align-items: center;
}
}
</style>

View File

@ -9,13 +9,16 @@
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
<v-container> <v-row no-gutters class="pr-4 pl-4">
<v-row no-gutters> <v-row no-gutters>
<v-col> <v-col class="align-start">
<CardButtonScore />
</v-col>
<v-col class="pl-3">
<card-solution /> <card-solution />
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-row>
</template> </template>
<script setup> <script setup>
@ -23,20 +26,52 @@
import CardSolution from '@/components/CardSolution.vue' import CardSolution from '@/components/CardSolution.vue'
import CardControl from '@/components/CardControl.vue' import CardControl from '@/components/CardControl.vue'
import CardSoundboard from '@/components/CardSoundboard.vue'; import CardSoundboard from '@/components/CardSoundboard.vue';
import CardButtonScore from '@/components/CardButtonScore.vue'
</script> </script>
<style> <style>
@media (min-width: 1024px) { @media (min-width: 1024px) {
.card__title.primary { .card__title.primary {
background-color: #e91e1e; /* Changez la couleur en fonction de votre thème */ background-color: #d42828; /* Changez la couleur en fonction de votre thème */
} }
.card__title.feedback { .card__title.feedback {
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */ background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
} }
.btn{ .btn{
border-radius:30px!important; border-radius:20px!important;
background-color: #e91e1e; /* Changez la couleur en fonction de votre thème */ }
.btn.red {
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
}
.btn.blue {
background-color: #2867d4; /* Changez la couleur en fonction de votre thème */
}
.btn.orange {
background-color: #d48f28; /* Changez la couleur en fonction de votre thème */
}
.btn.green {
background-color: #28d42e; /* Changez la couleur en fonction de votre thème */
}
.scorediv-style-red {
background-color: #d42828 !important;
padding: 15px;
border-top-left-radius: 10%;
}
.scorediv-style-orange {
background-color: #d48f28 !important;
padding: 15px;
border-bottom-left-radius: 10%;
}
.scorediv-style-blue {
background-color: #2867d4 !important;
padding: 15px;
border-top-right-radius: 10%;
}
.scorediv-style-green {
background-color: #28d42e !important;
padding: 15px;
border-bottom-right-radius: 10%;
} }
} }
</style> </style>

View File

@ -1,15 +1,53 @@
<template> <template>
<v-container> <v-container>
<v-btn @click="update"></v-btn> <v-row>
<v-col
v-for="image in images"
:key="image"
cols="12"
sm="6"
md="4"
>
<v-card>
<v-img
:src="`http://localhost:3000/images/${image}`"
:alt="image"
class="image-thumbnail"
/>
<v-card-title>{{ image }}</v-card-title>
</v-card>
</v-col>
</v-row>
</v-container> </v-container>
</template> </template>
<script setup> <script setup>
import { ref} from 'vue'; // Import des fonctions de Vue 3 import { ref, onMounted } from 'vue';
import variables from '@/variables.js';
function update() { // Déclare une variable réactive pour stocker les images
variables.RedScore = '10' const images = ref([]);
// Fonction pour récupérer les images depuis l'API
const fetchImages = async () => {
try {
const response = await fetch('http://localhost:3000/images-list');
const data = await response.json();
images.value = data; // Met à jour les images
} catch (error) {
console.error('Erreur lors de la récupération des images :', error);
} }
</script> };
// Appelle la fonction fetchImages lorsque le composant est monté
onMounted(() => {
fetchImages();
});
</script>
<style scoped>
.image-thumbnail {
max-width: 100%;
height: auto;
}
</style>

View File

@ -1,10 +1,6 @@
<template> <template>
<div> <div> <!-- Zone pour publier sur MQTT --> <PublishMQTTComponent />
<!-- Zone pour publier sur MQTT --> <!-- Zone pour afficher la console MQTT --> <MQTTConsoleComponent />
<PublishMQTTComponent />
<!-- Zone pour afficher la console MQTT -->
<MQTTConsoleComponent />
</div> </div>
</template> </template>
@ -13,9 +9,18 @@ import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT
export default { export default {
components: { components: { PublishMQTTComponent, // Composant pour publier sur MQTT MQTTConsoleComponent // Composant pour la console MQTT
PublishMQTTComponent, // Composant pour publier sur MQTT
MQTTConsoleComponent // Composant pour la console MQTT
} }
} }
</script> </script>
<style>
@media (min-width: 1024px) {
.card__title.primary { background-color: #d42828; /* Changez la couleur en fonction de votre thème */
}
.card__title.feedback { background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
}
.btn{ border-radius:30px!important; background-color: #d42828; /* Changez la couleur en fonction de votre thème */
}
}
</style>

View File

@ -1,36 +1,54 @@
<template> <template>
<v-label class="title-style-1">Paramètres</v-label>
<h1 class="title mb-4 ml-5 mt-5">Paramètres</h1>
<v-divider :thickness="2" class="border-opacity-100" color="primary"/> <v-divider :thickness="2" class="border-opacity-100" color="primary"/>
<h2 class="title ml-10 mb-5 mt-5">Son</h2> <v-label class="title-style-2">Son</v-label>
<div style="display: flex; align-items: center;"> <div class="mutltiple-per-line">
<v-switch label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/> <v-switch hide-details label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/>
<div style="width: 250px; margin-left: 16px;"> <div>
<v-slider class="ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/> <v-slider hide-details class="v-slider-style ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/>
</div> </div>
</div> </div>
<v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/> <v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/>
<v-divider /> <v-divider />
<h2 class="title ml-10 mb-5 mt-5">Affichage</h2> <v-label class="title-style-2">Affichage</v-label>
<v-switch label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15" color="primary"/> <div>
<v-divider /> <v-switch hide-details label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15 pb-3" color="primary"/>
<h2 class="title ml-10 mb-5 mt-5">MQTT</h2>
<div style="display: flex; align-items: center;">
<v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon>
<v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label>
</div> </div>
<v-divider /> <v-divider />
<v-label class="title-style-2">MQTT</v-label>
<div class="mutltiple-per-line">
<v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon>
<v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label>
<v-btn class="ml-10 mb-5" color="primary" @click="goToDebugRoute">Debugger</v-btn>
</div>
<v-divider />
<v-label class="title-style-2">Jeu</v-label>
<div class="mutltiple-per-line">
<v-switch hide-details label='Jouer le son de succès lorsque des points sont ajoutés' v-model="SuccessPlay" class="ml-15" color="primary"/>
</div>
<v-switch hide-details label='Jouer le son de erreur lorsque des points sont enlevés' v-model="ErrorPlay" class="ml-15" color="primary"/>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router';
const EmbeddedSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée. const EmbeddedSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
const EmbeddedSoundVolume = ref(50); // Définition d'une référence pour la case à cocher. Initialement décochée. const EmbeddedSoundVolume = ref(50); // Définition d'une référence pour la case à cocher. Initialement décochée.
const MQTTSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée. const MQTTSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
const MQTTBrokerState = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée. const MQTTBrokerState = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
const SattelitesDisplay = ref(false); const SattelitesDisplay = ref(false);
const SuccessPlay = ref(false);
const ErrorPlay = ref(false);
const router = useRouter();
const goToDebugRoute = () => {
router.push({
name: 'Debugger MQTT'
}); // Redirige vers la route nommée 'debugger'
};
onMounted(() => { onMounted(() => {
if (localStorage.getItem('EmbeddedSound')) { if (localStorage.getItem('EmbeddedSound')) {
@ -45,7 +63,14 @@
if (localStorage.getItem('SattelitesDisplay')) { if (localStorage.getItem('SattelitesDisplay')) {
SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch
} }
if (localStorage.getItem('SuccessPlay')) {
SuccessPlay.value = localStorage.getItem('SuccessPlay') === 'true'; // Added a default value for this switch
}
if (localStorage.getItem('ErrorPlay')) {
ErrorPlay.value = localStorage.getItem('ErrorPlay') === 'true'; // Added a default value for this switch
}
}); });
watch(EmbeddedSound, (EmbeddedSoundNewValue) => { watch(EmbeddedSound, (EmbeddedSoundNewValue) => {
if (EmbeddedSoundNewValue !== null) { if (EmbeddedSoundNewValue !== null) {
localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change. localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
@ -57,7 +82,6 @@
} }
}); });
watch(MQTTSound, (MQTTSoundNewValue) => { watch(MQTTSound, (MQTTSoundNewValue) => {
if (MQTTSoundNewValue !== null) { if (MQTTSoundNewValue !== null) {
localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change. localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
} }
@ -66,6 +90,43 @@
if (SattelitesDisplaynewValue !== null) { if (SattelitesDisplaynewValue !== null) {
localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch
} }
}); });
watch(SuccessPlay, (SuccessPlaynewValue) => {
if (SuccessPlaynewValue !== null) {
localStorage.setItem('SuccessPlay', SuccessPlaynewValue); // Added a default value for this switch
}
});
watch(ErrorPlay, (ErrorPlaynewValue) => {
if (ErrorPlaynewValue !== null) {
localStorage.setItem('ErrorPlay', ErrorPlaynewValue); // Added a default value for this switch
}
});
</script> </script>
<style>
.title-style-1{
margin-top: 20px;
margin-bottom: 16px;
margin-left: 20px;
font-size: 30px;
opacity: 100%;
font-weight: 500;
}
.title-style-2{
margin-top: 20px;
margin-bottom: 10px;
margin-left: 40px;
font-size: 25px;
opacity: 100%;
font-weight: 500;
}
.mutltiple-per-line{
display: flex;
align-items: center;
}
.v-slider-style{
width: 250px;
margin-left: 16px;
padding-top: px;
}
</style>

View File

@ -9,24 +9,9 @@ import { defineConfig } from 'vite'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [ vue({ template: { transformAssetUrls } }), Vuetify(), ViteFonts({ google: { families: [{ name: 'Roboto', styles: 'wght@100;300;400;500;700;900', }] } }),
vue({
template: { transformAssetUrls }
}),
Vuetify(),
ViteFonts({
google: {
families: [{
name: 'Roboto',
styles: 'wght@100;300;400;500;700;900',
}]
}
}),
], ],
define: { 'process.env': {} }, define: { 'process.env': {} },
resolve: { resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
} }
}) })