Compare commits
67 Commits
7d6c8d0f10
...
UIX-MQTT
Author | SHA1 | Date | |
---|---|---|---|
6c4c7f1bc4 | |||
08c8f85921 | |||
25ee22bfbf | |||
445a1b883f | |||
62bd29d752 | |||
dd72e02370 | |||
561b8b9820 | |||
ff1b546f0c | |||
1cf9266dcb | |||
2a5fb6c613 | |||
9147b40d47 | |||
fe7001effc | |||
9a47dc6633 | |||
20285b945f | |||
d0cc633264 | |||
bfcca3619f | |||
f2aa7e64a0 | |||
9801182db0 | |||
3f078aacb7 | |||
74f71a0ca3 | |||
08471a27b7 | |||
2cc97c8de8 | |||
3826a067e2 | |||
76383c1a3d | |||
8b012757f3 | |||
0dea30e8a6 | |||
da4daae323 | |||
ef4838bde1 | |||
1edb73bf5f | |||
5a983f2c7e | |||
96b7b1cbd7 | |||
080601b792 | |||
c3b86cb68b | |||
44ce39bf3f | |||
068e24ba60 | |||
995cca83ae | |||
87bdf08b65 | |||
c9f82674de | |||
ed78ffc158 | |||
83ce4b4fcc | |||
3036190701 | |||
e6a89c1561 | |||
553b37654e | |||
56bf47b91a | |||
745532c1b8 | |||
7e0687d3ca | |||
8a3185d694 | |||
340fbd3812 | |||
4c42f15dc6 | |||
ac2fb16703 | |||
ef29ef3d82 | |||
4d1f4ea120 | |||
6bfa1547f9 | |||
f3511277f9 | |||
de18d4957b | |||
c10bb714a9 | |||
117490fc3c | |||
ed23344d78 | |||
8e9f9d3825 | |||
b3c655df58 | |||
955956b13f | |||
f7ca05513c | |||
d57b2c39ee | |||
9f81c7c8c0 | |||
5aca026391 | |||
bbe2db581a | |||
0ce6139924 |
31
buzzer/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# buzzer
|
||||||
|
|
||||||
|
Code arduino du buzzer
|
||||||
|
|
||||||
|
## memo arduino-cli
|
||||||
|
|
||||||
|
Pour Compilation en ligne de commande.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Après installation d'arduino
|
||||||
|
|
||||||
|
```sh
|
||||||
|
arduino-cli config init
|
||||||
|
arduino-cli config add board_manager.additional_urls http://arduino.esp8266.com/stable/package_esp8266com_index.json
|
||||||
|
arduino-cli core update-index
|
||||||
|
arduino-cli core install esp8266:esp8266
|
||||||
|
arduino-cli lib install PubSubClient
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compilation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 buzzer.ino
|
||||||
|
```
|
||||||
|
|
||||||
|
### Envoi vers l'ESP
|
||||||
|
|
||||||
|
```sh
|
||||||
|
arduino-cli upload -p /dev/ttyUSB0 --fqbn esp8266:esp8266:nodemcuv2 buzzer.ino
|
||||||
|
```
|
68
buzzer/buzzer.ino
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
|
||||||
|
// Configurations WiFi et MQTT
|
||||||
|
const char* ssid = "Redmi Note 13 Pro 5G";
|
||||||
|
const char* password = "1234567890";
|
||||||
|
const char* mqtt_server = "192.168.127.208";
|
||||||
|
const char* mqtt_topic = "brainblast/buzzer/pressed/1";
|
||||||
|
const char* mqtt_message = "{\"buzzer_id\": 1, \"color\": \"#FF7518\"}";
|
||||||
|
|
||||||
|
// Déclaration des broches
|
||||||
|
#define BUTTON_PIN D8
|
||||||
|
|
||||||
|
WiFiClient espClient;
|
||||||
|
PubSubClient client(espClient);
|
||||||
|
|
||||||
|
void setup_wifi() {
|
||||||
|
delay(10);
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("Connexion au WiFi...");
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("");
|
||||||
|
Serial.println("WiFi connecté");
|
||||||
|
Serial.print("Adresse IP: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
void reconnect() {
|
||||||
|
while (!client.connected()) {
|
||||||
|
Serial.print("Connexion au broker MQTT...");
|
||||||
|
if (client.connect("ESP8266Client")) {
|
||||||
|
Serial.println("connecté");
|
||||||
|
} else {
|
||||||
|
Serial.print("échec, rc=");
|
||||||
|
Serial.print(client.state());
|
||||||
|
Serial.println("; nouvelle tentative dans 5 secondes");
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
setup_wifi();
|
||||||
|
client.setServer(mqtt_server, 1883);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (!client.connected()) {
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
client.loop();
|
||||||
|
|
||||||
|
// Vérifier si le bouton est pressé
|
||||||
|
if (digitalRead(BUTTON_PIN) == HIGH) {
|
||||||
|
Serial.println("Bouton pressé, envoi du message...");
|
||||||
|
client.publish(mqtt_topic, mqtt_message);
|
||||||
|
delay(200); // Anti-rebond pour éviter les publications multiples
|
||||||
|
}
|
||||||
|
}
|
13
index.html
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="fr">
|
|
||||||
<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>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"exclude": ["node_modules", "dist"]
|
|
||||||
}
|
|
223
services/buzzer-manager.js
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
// 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()
|
||||||
|
}));
|
||||||
|
// Notify the light manager to change to the team's color
|
||||||
|
client.publish('brainblast/light/change', JSON.stringify({
|
||||||
|
color: "#FFFFFF",
|
||||||
|
effect: 'rainbow'
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// 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...');
|
138
services/light-manager.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// Import du module MQTT
|
||||||
|
const mqtt = require('mqtt');
|
||||||
|
|
||||||
|
// Configuration du broker MQTT et de WLED
|
||||||
|
const brokerUrl = 'mqtt://localhost'; // Change ce lien si nécessaire
|
||||||
|
const clientId = 'light_manager_wled';
|
||||||
|
const wledTopicBase = 'wled/all'; // Le topic de base pour ton ruban WLED
|
||||||
|
const options = {
|
||||||
|
clientId,
|
||||||
|
clean: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// État des lumières
|
||||||
|
let currentColor = '#FFFFFF'; // Couleur par défaut (blanc)
|
||||||
|
let currentEffect = 'none'; // Pas d'effet par défaut
|
||||||
|
|
||||||
|
// Connexion au broker MQTT
|
||||||
|
const client = mqtt.connect(brokerUrl, options);
|
||||||
|
|
||||||
|
client.on('connect', () => {
|
||||||
|
console.log(`[INFO] Connected to MQTT broker ${brokerUrl} as ${clientId}`);
|
||||||
|
|
||||||
|
// Souscription aux topics de gestion de lumière
|
||||||
|
client.subscribe('brainblast/light/#', (err) => {
|
||||||
|
if (err) console.error('[ERROR] Subscription to light topics failed');
|
||||||
|
else console.log('[INFO] Successfully subscribed to light topics');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fonction pour envoyer un message au ruban WLED
|
||||||
|
function sendToWLED(topicSuffix, message) {
|
||||||
|
const wledTopic = `${wledTopicBase}/${topicSuffix}`;
|
||||||
|
client.publish(wledTopic, message, { qos: 1 }, (err) => {
|
||||||
|
if (err) console.error(`[ERROR] Failed to send message to WLED topic: ${wledTopic}`);
|
||||||
|
else console.log(`[INFO] Sent to WLED (${wledTopic}): ${message}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour appliquer un changement de lumière
|
||||||
|
function applyLightChange(color, effect, intensity) {
|
||||||
|
currentColor = color || currentColor;
|
||||||
|
currentEffect = effect || currentEffect;
|
||||||
|
|
||||||
|
console.log(`[INFO] Applying light change: Color=${currentColor}, Effect=${currentEffect}, Intensity=${intensity}`);
|
||||||
|
|
||||||
|
// Envoyer la couleur au ruban WLED
|
||||||
|
sendToWLED('col', currentColor);
|
||||||
|
|
||||||
|
// Appliquer l'effet si défini
|
||||||
|
if (currentEffect !== 'none') {
|
||||||
|
const effectId = getWLEDEffectId(currentEffect);
|
||||||
|
sendToWLED('api', "FX=" + effectId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Régler l'intensité si spécifiée
|
||||||
|
if (intensity) {
|
||||||
|
sendToWLED('api', intensity.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envoi de l'état mis à jour
|
||||||
|
sendLightStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour obtenir l'ID d'effet WLED correspondant à un effet donné
|
||||||
|
function getWLEDEffectId(effect) {
|
||||||
|
const effectsMap = {
|
||||||
|
'none': 0,
|
||||||
|
'blink': 1, // Effet de fondu
|
||||||
|
'fade': 12, // Clignotement
|
||||||
|
'rainbow': 9 // Effet arc-en-ciel
|
||||||
|
};
|
||||||
|
return effectsMap[effect] || 0; // Par défaut, aucun effet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour envoyer l'état actuel des lumières sur le topic de réponse
|
||||||
|
function sendLightStatus() {
|
||||||
|
const statusMessage = JSON.stringify({
|
||||||
|
status: "updated",
|
||||||
|
color: currentColor,
|
||||||
|
effect: currentEffect,
|
||||||
|
message: `Current light state: Color=${currentColor}, Effect=${currentEffect}`,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Envoi de l'état à tous les clients intéressés
|
||||||
|
client.publish('brainblast/light/status/response', statusMessage);
|
||||||
|
|
||||||
|
console.log('[INFO] Light status sent to brainblast/light/status/response');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestion des messages entrants
|
||||||
|
client.on('message', (topic, message) => {
|
||||||
|
let payload;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Analyse du message reçu
|
||||||
|
payload = JSON.parse(message.toString());
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[ERROR] Invalid JSON message received on topic ${topic}: ${message.toString()}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changement de lumière
|
||||||
|
if (topic === 'brainblast/light/change') {
|
||||||
|
const { color, effect, intensity } = payload;
|
||||||
|
|
||||||
|
// Valider la couleur et l'effet
|
||||||
|
if (!/^#[0-9A-F]{6}$/i.test(color)) {
|
||||||
|
console.error('[ERROR] Invalid color format');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appliquer le changement de lumière
|
||||||
|
applyLightChange(color, effect, intensity);
|
||||||
|
|
||||||
|
} else if (topic === 'brainblast/light/reset') {
|
||||||
|
// Réinitialisation des lumières à la couleur et l'effet par défaut
|
||||||
|
console.log('[INFO] Resetting lights to default state');
|
||||||
|
applyLightChange('#FFFFFF', 'reset', 255);
|
||||||
|
|
||||||
|
} else if (topic === 'brainblast/light/status/request') {
|
||||||
|
// Répondre à la requête de statut
|
||||||
|
console.log('[INFO] Light status request received');
|
||||||
|
sendLightStatus();
|
||||||
|
} else if (topic === 'brainblast/light/status/response') {
|
||||||
|
// Répondre à la requête de statut
|
||||||
|
console.log('[INFO] Light status response received');
|
||||||
|
} else {
|
||||||
|
console.error(`[ERROR] Unrecognized topic: ${topic}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestion des erreurs de connexion
|
||||||
|
client.on('error', (err) => {
|
||||||
|
console.error(`[ERROR] Error connecting to broker: ${err}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[INFO] Light Manager with WLED support and status handling started...');
|
497
services/package-lock.json
generated
Normal 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
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mqtt": "^5.10.1"
|
||||||
|
}
|
||||||
|
}
|
161
services/test-buzzer-manager.js
Normal 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);
|
||||||
|
}
|
BIN
soundplayer-mqtt/assets/sounds/8-bit-coin-fx_G_minor.wav
Normal file
1
soundplayer-mqtt/assets/sounds/bell.wav
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
8-bit-coin-fx_G_minor.wav
|
1
soundplayer-mqtt/assets/sounds/coin.wav
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
8-bit-coin-fx_G_minor.wav
|
BIN
soundplayer-mqtt/assets/sounds/fail.mp3
Normal file
1
soundplayer-mqtt/config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
mqttBrokerUrl = 'ws://localhost:9001'
|
482
soundplayer-mqtt/package-lock.json
generated
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
soundplayer-mqtt/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mqtt": "^5.3.6",
|
||||||
|
"play-sound": "^1.1.6"
|
||||||
|
}
|
||||||
|
}
|
54
soundplayer-mqtt/soundplayer.js
Normal 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);
|
||||||
|
});
|
27
src/App.vue
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-app>
|
|
||||||
<BrainBlastBar />
|
|
||||||
<GameStatus />
|
|
||||||
<v-main>
|
|
||||||
<RouterView />
|
|
||||||
</v-main>
|
|
||||||
<v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer>
|
|
||||||
</v-app>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
import BrainBlastBar from '@/components/BrainBlastBar.vue'
|
|
||||||
import GameStatus from '@/components/GameStatus.vue'
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.footer {
|
|
||||||
position: fixed; /* Fixe le footer en bas de la page */
|
|
||||||
bottom: 0; /* Aligne le footer en bas de la page */
|
|
||||||
left: 0; /* Aligne le footer à gauche */
|
|
||||||
width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */
|
|
||||||
z-index: 1000; /* Assure que le footer est au-dessus des autres éléments */
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
|
Before Width: | Height: | Size: 276 B |
@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-app-bar :elevation="12">
|
|
||||||
<RouterMenu />
|
|
||||||
<v-app-bar-title>Brain Blast</v-app-bar-title>
|
|
||||||
|
|
||||||
<template v-slot:append>
|
|
||||||
<v-btn @click="toggleTheme" icon="mdi-theme-light-dark"></v-btn>
|
|
||||||
</template>
|
|
||||||
</v-app-bar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useTheme } from 'vuetify'
|
|
||||||
import RouterMenu from '@/components/RouterMenu.vue'
|
|
||||||
|
|
||||||
const theme = useTheme()
|
|
||||||
|
|
||||||
function toggleTheme () {
|
|
||||||
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-navigation-drawer>
|
|
||||||
<v-list-item title="Brain Blast" subtitle="The cultural quizzzz"></v-list-item>
|
|
||||||
<v-divider></v-divider>
|
|
||||||
<v-list-item link title="List Item 1"></v-list-item>
|
|
||||||
<v-list-item link title="List Item 2"></v-list-item>
|
|
||||||
<v-list-item link title="List Item 3"></v-list-item>
|
|
||||||
</v-navigation-drawer>
|
|
||||||
</template>
|
|
@ -1,23 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Console MQTT</h1>
|
|
||||||
<div v-for="(message, index) in messages" :key="index">{{ message }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { subscribeToTopic } from '@/services/mqttService'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
messages: [] // Initialiser un tableau pour stocker les messages MQTT
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
subscribeToTopic('#', (topic, message) => {
|
|
||||||
this.messages.push(`Topic: ${topic}, Message: ${message}`) // Ajouter le message à la liste des messages
|
|
||||||
}) // S'abonner à tous les topics MQTT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,40 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Publier sur MQTT</h1>
|
|
||||||
<select v-model="selectedTopic">
|
|
||||||
<option v-for="topic in topics" :key="topic">{{ topic }}</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" v-model="message" placeholder="Saisissez votre message" />
|
|
||||||
<button @click="publishMessage">Publier sur MQTT</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { publishMessage } from '@/services/mqttService'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
message: '', // Initialiser la variable message
|
|
||||||
selectedTopic: 'topic1',
|
|
||||||
topics: [
|
|
||||||
'topic1',
|
|
||||||
'topic2',
|
|
||||||
'topic3',
|
|
||||||
'topic4',
|
|
||||||
'topic5',
|
|
||||||
'topic6',
|
|
||||||
'topic7',
|
|
||||||
'topic8',
|
|
||||||
'topic9',
|
|
||||||
'topic10'
|
|
||||||
] // Liste des topics
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
publishMessage() {
|
|
||||||
publishMessage(this.selectedTopic, this.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,24 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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-list>
|
|
||||||
<v-list-item v-for="route in routes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-menu>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const routes = router.options.routes
|
|
||||||
|
|
||||||
let menu = ref(false)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.menu-below-bar {
|
|
||||||
margin-top: 48px; /* La hauteur de la barre d'application */
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,86 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="item">
|
|
||||||
<i>
|
|
||||||
<slot name="icon"></slot>
|
|
||||||
</i>
|
|
||||||
<div class="details">
|
|
||||||
<h3>
|
|
||||||
<slot name="heading"></slot>
|
|
||||||
</h3>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.item {
|
|
||||||
margin-top: 2rem;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
place-content: center;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 0.4rem;
|
|
||||||
color: var(--color-heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.item {
|
|
||||||
margin-top: 0;
|
|
||||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
top: calc(50% - 25px);
|
|
||||||
left: -26px;
|
|
||||||
position: absolute;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
background: var(--color-background);
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:before {
|
|
||||||
content: ' ';
|
|
||||||
border-left: 1px solid var(--color-border);
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: calc(50% + 25px);
|
|
||||||
height: calc(50% - 25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:after {
|
|
||||||
content: ' ';
|
|
||||||
border-left: 1px solid var(--color-border);
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: calc(50% + 25px);
|
|
||||||
height: calc(50% - 25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:first-of-type:before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:last-of-type:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,38 +0,0 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
|
||||||
import HomeView from '../views/HomeView.vue'
|
|
||||||
|
|
||||||
const router = createRouter({
|
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'Home',
|
|
||||||
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',
|
|
||||||
name: 'Game Control (Présentateur)',
|
|
||||||
component: () => import('@/views/GameControl.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/game/display',
|
|
||||||
name: 'Game Display (Projection)',
|
|
||||||
component: () => import('@/views/GameDisplay.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/mqtt-debugger',
|
|
||||||
name: 'Debugger MQTT',
|
|
||||||
component: () => import('@/views/MQTTDebugView.vue')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* plugins/vuetify.js
|
|
||||||
*
|
|
||||||
* Framework documentation: https://vuetifyjs.com`
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Styles
|
|
||||||
import '@mdi/font/css/materialdesignicons.css'
|
|
||||||
import 'vuetify/styles'
|
|
||||||
|
|
||||||
// Composables
|
|
||||||
import { createVuetify } from 'vuetify'
|
|
||||||
|
|
||||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
|
||||||
export default createVuetify({
|
|
||||||
theme: {
|
|
||||||
defaultTheme: 'dark'
|
|
||||||
}
|
|
||||||
})
|
|
@ -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>
|
|
@ -1,5 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Bienvenue sur BrainBlast !</h1>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- Zone pour publier sur MQTT -->
|
|
||||||
<PublishMQTTComponent />
|
|
||||||
|
|
||||||
<!-- Zone pour afficher la console MQTT -->
|
|
||||||
<MQTTConsoleComponent />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer le composant pour publier sur MQTT
|
|
||||||
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
PublishMQTTComponent, // Composant pour publier sur MQTT
|
|
||||||
MQTTConsoleComponent // Composant pour la console MQTT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
7
ui/index.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<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>
|
||||||
|
</head>
|
||||||
|
<body> <div id="app"></div> <script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
ui/jsconfig.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": { "paths": { "@/*": ["./src/*"] }
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
1630
package-lock.json → ui/package-lock.json
generated
@ -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,20 +13,24 @@
|
|||||||
},
|
},
|
||||||
"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",
|
||||||
"unplugin-fonts": "^1.1.1",
|
"unplugin-fonts": "^1.1.1",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.1.6",
|
||||||
"vite-plugin-vuetify": "^2.0.1"
|
"vite-plugin-vuetify": "^2.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
27
ui/src/App.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<v-app>
|
||||||
|
<BrainBlastBar />
|
||||||
|
<GameStatus v-if="$route.name === 'Game Control (Présentateur)'">
|
||||||
|
</GameStatus>
|
||||||
|
<v-main>
|
||||||
|
<RouterView />
|
||||||
|
</v-main> <!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import BrainBlastBar from '@/components/BrainBlastBar.vue'
|
||||||
|
import GameStatus from '@/components/GameStatus.vue'
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.footer {
|
||||||
|
position: fixed; /* Fixe le footer en bas de la page */
|
||||||
|
bottom: 0; /* Aligne le footer en bas de la page */
|
||||||
|
left: 0; /* Aligne le footer à gauche */
|
||||||
|
width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */
|
||||||
|
z-index: 1000; /* Assure que le footer est au-dessus des autres éléments */
|
||||||
|
}
|
||||||
|
</style>
|
BIN
ui/src/assets/BrainBlast-Ai-Upscaled.jpg
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
ui/src/assets/BrainBlast-For-HomeView-Alpha.png
Normal file
After Width: | Height: | Size: 11 MiB |
BIN
ui/src/assets/BrainBlast-For-HomeView.jpg
Normal file
After Width: | Height: | Size: 768 KiB |
BIN
ui/src/assets/copilot-solution-.jpg
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
ui/src/assets/copilot-solution-FULL-HD.jpg
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ui/src/assets/copilot-solution-UPSCALE.jpg
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
ui/src/assets/design.png
Normal file
After Width: | Height: | Size: 3.4 MiB |
10
ui/src/components/BrainBlastBar.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<v-app-bar :elevation="5" height="50">
|
||||||
|
<RouterMenu />
|
||||||
|
<v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title>
|
||||||
|
</v-app-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import RouterMenu from '@/components/RouterMenu.vue'
|
||||||
|
</script>
|
114
ui/src/components/CardButtonScore.vue
Normal 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>
|
34
ui/src/components/CardControl.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<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-camera-control</v-icon>
|
||||||
|
Contrôle du jeu
|
||||||
|
</v-card-title>
|
||||||
|
<v-container class="text-center">
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||||
|
<mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous">
|
||||||
|
<v-icon left size="60">mdi-skip-previous</v-icon>
|
||||||
|
</mqtt-button>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||||
|
<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-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>
|
45
ui/src/components/CardScore.vue
Normal 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>
|
46
ui/src/components/CardSolution.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<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-play-network-outline</v-icon>
|
||||||
|
Solution </v-card-title>
|
||||||
|
<v-container class="text-center">
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-container class="text-center"> <!-- 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>
|
||||||
|
</v-container>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.image-container {
|
||||||
|
width: 300px; overflow: hidden; /* Pour masquer le dépassement de l'image */
|
||||||
|
border: 1px solid #ccc; /* Bordure de l'image */
|
||||||
|
}
|
||||||
|
.image-container img {
|
||||||
|
width: 100%; /* Pour remplir complètement le conteneur */
|
||||||
|
height: auto; /* Pour maintenir le ratio d'aspect de l'image */
|
||||||
|
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>
|
||||||
|
|
||||||
|
<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>
|
61
ui/src/components/CardSoundboard.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<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-music-box-multiple</v-icon>
|
||||||
|
Soundboard
|
||||||
|
</v-card-title>
|
||||||
|
<v-container class="text-center">
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||||
|
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded>
|
||||||
|
<v-icon size="60">mdi-check-circle-outline</v-icon>
|
||||||
|
</mqtt-button>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||||
|
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded>
|
||||||
|
<v-icon size="60">mdi-close-circle-outline</v-icon>
|
||||||
|
</mqtt-button>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||||
|
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded>
|
||||||
|
<v-icon size="60">mdi-timer-outline</v-icon>
|
||||||
|
</mqtt-button>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||||
|
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded>
|
||||||
|
<v-icon size="60">mdi-human-handsup</v-icon>
|
||||||
|
</mqtt-button>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||||
|
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded>
|
||||||
|
<v-icon size="60">mdi-bell-outline</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 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>
|
100
ui/src/components/CardTimer.vue
Normal 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>
|
41
ui/src/components/GameStatus.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<v-navigation-drawer width="250"> <!-- Augmenter la largeur pour deux équipes côte à côte -->
|
||||||
|
<div class="label-pos">
|
||||||
|
<v-label class="labelTitle-style">Buzzer connectés</v-label>
|
||||||
|
</div>
|
||||||
|
<v-row no-gutters justify="space-around" class="button-pos">
|
||||||
|
<v-icon color="BuzzerRed">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="BuzzerGreen">mdi-radiobox-marked</v-icon>
|
||||||
|
</v-row>
|
||||||
|
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||||
|
<CardScore/>
|
||||||
|
<CardTimer/>
|
||||||
|
</v-navigation-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import CardTimer from '@/components/CardTimer.vue'
|
||||||
|
import CardScore from '@/components/CardScore.vue'
|
||||||
|
import { ref } from 'vue'; // Import des fonctions de Vue 3
|
||||||
|
import variables from '@/variables.js';
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.label-pos {
|
||||||
|
padding-top: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.labelTitle-style {
|
||||||
|
font-size: 20px !important;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #e91e1e !important;
|
||||||
|
opacity: 90% !important;
|
||||||
|
}
|
||||||
|
.button-pos {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
95
ui/src/components/MQTTColorPublisher.vue
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<v-container class="v-container-style">
|
||||||
|
<v-card tile outlined width="500">
|
||||||
|
<v-card-title class="card__title primary centered-title">
|
||||||
|
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
|
||||||
|
Publier une couleur
|
||||||
|
</v-card-title>
|
||||||
|
<div class="input-style">
|
||||||
|
<v-select label="Topic" v-model="selectedTopic" :items="topics" prepend-icon="mdi-target"></v-select>
|
||||||
|
</div>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="6" class="color-picker-style">
|
||||||
|
<div>
|
||||||
|
<v-color-picker mode="hex" v-model="selectedColor" border="md" width="250"></v-color-picker>
|
||||||
|
<v-btn class="v-btn-style-validate" height="35" @click="publisCustomColor">Publier</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="5.5" class="button-container">
|
||||||
|
<v-btn color="#D42828" class="team-button" @click="publisButtonColor('#D42828')">Team Rouge</v-btn>
|
||||||
|
<v-btn color="#FF7518" class="team-button" @click="publisButtonColor('#FF7518')">Team Orange</v-btn>
|
||||||
|
<v-btn color="#00FF1F" class="team-button" @click="publisButtonColor('#00FF1F')">Team Verte</v-btn>
|
||||||
|
<v-btn color="#007AFF" class="team-button" @click="publisButtonColor('#007AFF')">Team Bleue</v-btn>
|
||||||
|
<v-btn color="#FFFC00" class="team-button" @click="publisButtonColor('#FFFC00')">Team Jaune</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { publishMessage } from '@/services/mqttService'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: '', // Initialiser la variable message
|
||||||
|
selectedTopic: 'Selectionnez un topic',
|
||||||
|
selectedColor: "#FF0000",
|
||||||
|
topics: [
|
||||||
|
'/wled/all',
|
||||||
|
'/wled/1',
|
||||||
|
'/wled/2',
|
||||||
|
'/wled/3',
|
||||||
|
'/wled/4',
|
||||||
|
'/wled/5'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
publisCustomColor() {
|
||||||
|
publishMessage(this.selectedTopic, this.selectedColor)
|
||||||
|
},
|
||||||
|
publisButtonColor(param) {
|
||||||
|
publishMessage(this.selectedTopic, param)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.v-container-style {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.input-style{
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
.v-btn-style-validate{
|
||||||
|
border-top-right-radius: 0%;
|
||||||
|
border-top-left-radius: 0%;
|
||||||
|
}
|
||||||
|
.centered-title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.color-picker-style {
|
||||||
|
margin-left: 5%;
|
||||||
|
margin-bottom: 5%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.button-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 27px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 13px;
|
||||||
|
}
|
||||||
|
.team-button {
|
||||||
|
width: 140px;
|
||||||
|
display: block;
|
||||||
|
margin: 12% auto 0; /* 5% de marge en bas pour espacer les boutons */
|
||||||
|
background-color: #d42828;
|
||||||
|
}
|
||||||
|
</style>
|
72
ui/src/components/MQTTDebugConsole.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<v-container class="v-container-style-console">
|
||||||
|
<v-card tile outlined width="500">
|
||||||
|
<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 class="v-label-topic-message-title">Topic : </v-label><v-label class="v-label-topic-message">{{ log.topic }} </v-label>
|
||||||
|
<v-label class="v-label-topic-message-title">Msg : </v-label><v-label class="v-label-topic-message">{{ log.message }}</v-label>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { subscribeToTopic } from '@/services/mqttService'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() { return {
|
||||||
|
messageLogs: [] // Initialiser un tableau pour stocker les messages MQTT avec horodatage
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() { subscribeToTopic('#', (topic, message) => { // Obtenir l'horodatage actuel
|
||||||
|
const timestamp = new Date().toLocaleString('fr-FR', {
|
||||||
|
hour12: false,
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter le message avec l'horodatage à la liste des messages
|
||||||
|
this.messageLogs.push({ timestamp, topic: `${topic}`, message: `${message}` });
|
||||||
|
//this.messageLogs.push({ timestamp, message: `${message}` });
|
||||||
|
|
||||||
|
// Limiter la liste à 10 messages
|
||||||
|
if (this.messageLogs.length > 26) {
|
||||||
|
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
|
||||||
|
} }) // S'abonner à tous les topics MQTT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.v-container-style-console {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: sticky;
|
||||||
|
top: 50px; /* Distance depuis le haut de la fenêtre avant de "coller" */
|
||||||
|
}
|
||||||
|
.centered-title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v-label-timestamp{
|
||||||
|
opacity: 100%;
|
||||||
|
font-style: oblique;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #838383;
|
||||||
|
}
|
||||||
|
.v-label-topic-message-title{
|
||||||
|
opacity: 100%;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #d42828;
|
||||||
|
}
|
||||||
|
.v-label-topic-message{
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
68
ui/src/components/MQTTDebugPublish.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<v-container class="v-container-style">
|
||||||
|
<v-card tile outlined width="500">
|
||||||
|
<v-card-title class="card__title primary centered-title">
|
||||||
|
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
|
||||||
|
Publier un message
|
||||||
|
</v-card-title>
|
||||||
|
<div class="input-style">
|
||||||
|
<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-standalone" height="40" @click="publishBuzzerUnblock">Déblocage<br>Buzzer</v-btn>
|
||||||
|
<v-btn class="v-btn-style-validate" height="50" @click="publisCustomMessage">Publier</v-btn>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { publishMessage } from '@/services/mqttService'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() { return { message: '', // Initialiser la variable message
|
||||||
|
selectedTopic: 'Selectionnez un topic',
|
||||||
|
topics: [
|
||||||
|
'/wled/all',
|
||||||
|
'/display/control',
|
||||||
|
'/sound/playsound'
|
||||||
|
] // Liste des topics
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
publisCustomMessage() {
|
||||||
|
publishMessage(this.selectedTopic, this.message)
|
||||||
|
},
|
||||||
|
publishBuzzerUnblock() {
|
||||||
|
publishMessage('brainblast/buzzer/unlock', "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.v-container-style {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.input-style{
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
.v-btn-style-standalone{
|
||||||
|
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||||
|
margin-bottom: 5%;
|
||||||
|
margin-left: 5%;
|
||||||
|
}
|
||||||
|
.v-btn-style-validate{
|
||||||
|
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>
|
22
ui/src/components/MqttButton.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<v-btn @click="_publishMessage" v-bind="$attrs">
|
||||||
|
<slot/>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { publishMessage } from '@/services/mqttService'
|
||||||
|
import { ref, defineProps } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
topic: String,
|
||||||
|
message: null
|
||||||
|
})
|
||||||
|
|
||||||
|
const disabled = ref(false)
|
||||||
|
|
||||||
|
const _publishMessage = () => {
|
||||||
|
publishMessage(props.topic, JSON.stringify(props.message))
|
||||||
|
disabled.value = true
|
||||||
|
}
|
||||||
|
</script>
|
29
ui/src/components/RouterMenu.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<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-list>
|
||||||
|
<v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const routes = router.options.routes;
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.menu-below-bar {
|
||||||
|
margin-top: 48px; /* La hauteur de la barre d'application */
|
||||||
|
}
|
||||||
|
</style>
|
16
ui/src/config.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Fichier vide, regarde config.js.example pour personaliser ce fichier.
|
||||||
|
// Note de dev : Normalement ce fichier ne devrait plus avoir de
|
||||||
|
// modifications
|
||||||
|
|
||||||
|
// config.js
|
||||||
|
export default {
|
||||||
|
mqttBrokerUrl: 'ws://192.168.1.78:9001',
|
||||||
|
|
||||||
|
// Buzzer
|
||||||
|
redBuzzerIP: '',
|
||||||
|
blueBuzzerIP: '',
|
||||||
|
orangeBuzzerIP: '',
|
||||||
|
greenBuzzerIP: ''
|
||||||
|
|
||||||
|
// Light
|
||||||
|
};
|
@ -4,7 +4,6 @@ import { createApp } from 'vue'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
registerPlugins(app)
|
registerPlugins(app)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
33
ui/src/plugins/router.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [ {
|
||||||
|
path: '/',
|
||||||
|
name: 'Accueil',
|
||||||
|
component: HomeView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/game/control',
|
||||||
|
name: 'Game Control (Présentateur)',
|
||||||
|
component: () => import('@/views/GameControl.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/game/display',
|
||||||
|
name: 'Game Display (Projection)',
|
||||||
|
component: () => import('@/views/GameDisplay.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/mqtt-debugger',
|
||||||
|
name: 'Debugger MQTT',
|
||||||
|
component: () => import('@/views/MQTTDebugView.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'Paramètres',
|
||||||
|
component: () => import('@/views/SettingsView.vue') }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
57
ui/src/plugins/vuetify.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* plugins/vuetify.js
|
||||||
|
*
|
||||||
|
* Framework documentation: https://vuetifyjs.com`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
import '@mdi/font/css/materialdesignicons.css'
|
||||||
|
import 'vuetify/styles'
|
||||||
|
|
||||||
|
// Composables
|
||||||
|
import { createVuetify } from 'vuetify'
|
||||||
|
|
||||||
|
const CustomThemeDark = {
|
||||||
|
dark: true,
|
||||||
|
colors: {
|
||||||
|
background: '#121212',
|
||||||
|
primary: '#d42828',
|
||||||
|
secondary: '#F44336',
|
||||||
|
accent: '#FFC107',
|
||||||
|
error: '#e91e1e',
|
||||||
|
warning: '#FFC107',
|
||||||
|
info: '#607D8B',
|
||||||
|
success: '#e91e1e',
|
||||||
|
BuzzerBlue: '#2867d4',
|
||||||
|
BuzzerOrange: '#d48f28',
|
||||||
|
BuzzerRed: '#d42828',
|
||||||
|
BuzzerGreen: '#28d42e',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const CustomThemeLight = {
|
||||||
|
dark: false,
|
||||||
|
colors: {
|
||||||
|
background: '#ffffff',
|
||||||
|
primary: '#d42828',
|
||||||
|
secondary: '#F44336',
|
||||||
|
accent: '#FFC107',
|
||||||
|
error: '#e91e1e',
|
||||||
|
warning: '#FFC107',
|
||||||
|
info: '#607D8B',
|
||||||
|
success: '#4CAF50',
|
||||||
|
BuzzerBlue: '#2867d4',
|
||||||
|
BuzzerOrange: '#d48f28',
|
||||||
|
BuzzerRed: '#d42828',
|
||||||
|
BuzzerGreen: '#28d42e',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||||
|
export default createVuetify({
|
||||||
|
theme: {
|
||||||
|
defaultTheme: 'CustomThemeDark',
|
||||||
|
themes: {
|
||||||
|
CustomThemeDark,
|
||||||
|
CustomThemeLight, },
|
||||||
|
},
|
||||||
|
})
|
BIN
ui/src/quizz/geography-history/Q-1.jpeg
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
ui/src/quizz/geography-history/Q-2.jpeg
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ui/src/quizz/geography-history/Q-3.jpeg
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
ui/src/quizz/geography-history/Q-4.jpeg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
ui/src/quizz/geography-history/Q-5.jpeg
Normal file
After Width: | Height: | Size: 1.9 MiB |
27
ui/src/quizz/geography-history/config.yml
Normal 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"
|
BIN
ui/src/quizz/geography-history/originales/Q-1.webp
Normal file
After Width: | Height: | Size: 447 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-2.webp
Normal file
After Width: | Height: | Size: 460 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-3.webp
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-4.webp
Normal file
After Width: | Height: | Size: 355 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-5.webp
Normal file
After Width: | Height: | Size: 521 KiB |
0
ui/src/quizz/nature-animals/config.yml
Normal file
0
ui/src/quizz/science-technology/config.yml
Normal file
0
ui/src/quizz/sport/config.yml
Normal file
BIN
ui/src/quizz/video-games/Original/Q-1.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
ui/src/quizz/video-games/Original/Q-2.jpg
Normal file
After Width: | Height: | Size: 445 KiB |
BIN
ui/src/quizz/video-games/Original/Q-3.webp
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
ui/src/quizz/video-games/Original/Q-4.webp
Normal file
After Width: | Height: | Size: 389 KiB |
BIN
ui/src/quizz/video-games/Original/Q-5.webp
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
ui/src/quizz/video-games/Q-1.jpeg
Normal file
After Width: | Height: | Size: 3.1 MiB |
BIN
ui/src/quizz/video-games/Q-2.jpeg
Normal file
After Width: | Height: | Size: 4.1 MiB |
BIN
ui/src/quizz/video-games/Q-3.jpeg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
ui/src/quizz/video-games/Q-4.jpeg
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
ui/src/quizz/video-games/Q-5.jpeg
Normal file
After Width: | Height: | Size: 3.0 MiB |
27
ui/src/quizz/video-games/config.yml
Normal 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: "C’est 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"
|
64
ui/src/services/buzzerWatcher.js
Normal 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;
|
@ -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())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
43
ui/src/services/pictureEngine.js
Normal 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}`);
|
||||||
|
});
|
27
ui/src/variables.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export default {
|
||||||
|
// Gestion des score et des Buzzers
|
||||||
|
|
||||||
|
// Scores totaux
|
||||||
|
RedTotalScore: 11,
|
||||||
|
BlueTotalScore: 22,
|
||||||
|
GreenTotalScore: 33,
|
||||||
|
OrangeTotalScore: 44,
|
||||||
|
|
||||||
|
// Score de la manche courante
|
||||||
|
RedRoundScore: 1,
|
||||||
|
BlueRoundScore: 2,
|
||||||
|
OrangeRoundScore: 3,
|
||||||
|
GreenRoundScore: 4,
|
||||||
|
|
||||||
|
//Etat des buzzer
|
||||||
|
BuzzerRed: false,
|
||||||
|
BuzzerBlue: false,
|
||||||
|
BuzzerOrange: false,
|
||||||
|
BuzzerGreen: false,
|
||||||
|
|
||||||
|
// Ajoutez d'autres variables globales ici
|
||||||
|
};
|
||||||
|
|
||||||
|
// Variables localStorage
|
||||||
|
export const localStorageVars = { // Exemple de variable localStorage RedScorelocal: localStorage.getItem('RedScore') || '', BlueScorelocal: localStorage.getItem('BlueScore') || '', OrangeScorelocal: localStorage.getItem('OrangeScore') || '', GreenScorelocal: localStorage.getItem('GreenScore') || '',
|
||||||
|
};
|
77
ui/src/views/GameControl.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<v-row no-gutters>
|
||||||
|
<v-col class="align-start">
|
||||||
|
<card-control />
|
||||||
|
</v-col>
|
||||||
|
<v-col class="pl-3">
|
||||||
|
<card-soundboard />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
<v-row no-gutters class="pr-4 pl-4">
|
||||||
|
<v-row no-gutters>
|
||||||
|
<v-col class="align-start">
|
||||||
|
<CardButtonScore />
|
||||||
|
</v-col>
|
||||||
|
<v-col class="pl-3">
|
||||||
|
<card-solution />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import CardSolution from '@/components/CardSolution.vue'
|
||||||
|
import CardControl from '@/components/CardControl.vue'
|
||||||
|
import CardSoundboard from '@/components/CardSoundboard.vue';
|
||||||
|
import CardButtonScore from '@/components/CardButtonScore.vue'
|
||||||
|
|
||||||
|
</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:20px!important;
|
||||||
|
}
|
||||||
|
.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>
|
53
ui/src/views/GameDisplay.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
// Déclare une variable réactive pour stocker les images
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Appelle la fonction fetchImages lorsque le composant est monté
|
||||||
|
onMounted(() => {
|
||||||
|
fetchImages();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.image-thumbnail {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
3
ui/src/views/HomeView.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<v-img src="../assets/BrainBlast-For-HomeView-Alpha.png" class="fill-height"></v-img>
|
||||||
|
</template>
|
53
ui/src/views/MQTTDebugView.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<v-row>
|
||||||
|
|
||||||
|
</v-row>
|
||||||
|
<div> <!-- Zone pour publier sur MQTT -->
|
||||||
|
<!-- Zone pour afficher la console MQTT -->
|
||||||
|
</div>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<!-- Colonne gauche avec les deux composants empilés -->
|
||||||
|
<v-col cols="6">
|
||||||
|
<!-- Composant 1 -->
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<PublishMQTTComponent />
|
||||||
|
<MQTTColorPublisher />
|
||||||
|
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- Colonne droite avec le composant unique -->
|
||||||
|
<v-col cols="6">
|
||||||
|
<MQTTConsoleComponent />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer le composant pour publier sur MQTT
|
||||||
|
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour publier sur MQTT
|
||||||
|
import MQTTColorPublisher from '@/components/MQTTColorPublisher.vue' // Importer le composant pour publier sur MQTT
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PublishMQTTComponent, // Composant pour publier sur MQTT MQTTConsoleComponent // Composant pour la console MQTT
|
||||||
|
MQTTConsoleComponent, // Composant pour la console MQTT
|
||||||
|
MQTTColorPublisher, // Composant pour publier une couleur custom dans un topic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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>
|
132
ui/src/views/SettingsView.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<v-label class="title-style-1">Paramètres</v-label>
|
||||||
|
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||||
|
<v-label class="title-style-2">Son</v-label>
|
||||||
|
<div class="mutltiple-per-line">
|
||||||
|
<v-switch hide-details label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/>
|
||||||
|
<div>
|
||||||
|
<v-slider hide-details class="v-slider-style ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/>
|
||||||
|
<v-divider />
|
||||||
|
<v-label class="title-style-2">Affichage</v-label>
|
||||||
|
<div>
|
||||||
|
<v-switch hide-details label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15 pb-3" color="primary"/>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
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 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 MQTTBrokerState = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
|
||||||
|
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(() => {
|
||||||
|
if (localStorage.getItem('EmbeddedSound')) {
|
||||||
|
EmbeddedSound.value = localStorage.getItem('EmbeddedSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||||
|
}
|
||||||
|
if (localStorage.getItem('MQTTSound')) {
|
||||||
|
MQTTSound.value = localStorage.getItem('MQTTSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||||
|
}
|
||||||
|
if (localStorage.getItem('EmbeddedSoundVolume')) {
|
||||||
|
EmbeddedSoundVolume.value = localStorage.getItem('EmbeddedSoundVolume'); // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||||
|
}
|
||||||
|
if (localStorage.getItem('SattelitesDisplay')) {
|
||||||
|
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) => {
|
||||||
|
if (EmbeddedSoundNewValue !== null) {
|
||||||
|
localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watch(EmbeddedSoundVolume, (EmbeddedSoundVolumeNewValue) => {
|
||||||
|
if (EmbeddedSoundVolumeNewValue !== null) {
|
||||||
|
localStorage.setItem('EmbeddedSoundVolume', EmbeddedSoundVolumeNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watch(MQTTSound, (MQTTSoundNewValue) => {
|
||||||
|
if (MQTTSoundNewValue !== null) {
|
||||||
|
localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watch(SattelitesDisplay, (SattelitesDisplaynewValue) => {
|
||||||
|
if (SattelitesDisplaynewValue !== null) {
|
||||||
|
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>
|
||||||
|
|
||||||
|
<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>
|