1 Commits

Author SHA1 Message Date
b73865157d test 2024-02-27 08:53:18 +00:00
99 changed files with 692 additions and 3099 deletions

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!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>

8
jsconfig.json Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,54 +0,0 @@
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 Normal file
View File

@ -0,0 +1,27 @@
<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>

View File

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 768 KiB

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 276 B

View File

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

View File

@ -0,0 +1,36 @@
<template>
<v-card tile outlined class="card">
<v-card-title class="card__title primary">
<v-icon left class="white--text pr-5 pl-2" size="50">mdi-camera-control</v-icon>
Contrôles
</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="240" height="130" class="btn xs12 sm6 md3" topic="/display/control" message="previous">
<v-icon left size="100">mdi-skip-previous</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3" topic="/display/control" message="next">
<v-icon left size="100">mdi-skip-next</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3" topic="/display/control" message="pause">
<v-icon left size="100">mdi-pause</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3 " topic="/display/control" message="play">
<v-icon left size="100">mdi-play</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script setup>
import MqttButton from './MqttButton.vue';
</script>

View File

@ -0,0 +1,30 @@
<template>
<v-card tile outlined class="card">
<v-card-title class="card__title feedback">
<v-icon left class="white--text pr-5 pl-2" size="50">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="https://c4.wallpaperflare.com/wallpaper/908/893/291/funny-middle-finger-black-background-wallpaper-preview.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 */
}
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<v-card tile outlined class="card" color="">
<v-card-title class="card__title primary">
<v-icon left class="white--text pr-5 pl-2" size="50">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" width="200" height="130" topic="/sound/playsound" message="good-response" rounded>
<v-icon size="100">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" width="200" height="130" topic="/sound/playsound" message="bad-response" rounded>
<v-icon size="100">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" width="200" height="130" topic="/sound/playsound" message="timer" rounded>
<v-icon size="100">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" width="220" height="130" topic="/sound/playsound" message="applause" rounded>
<v-icon size="100">mdi-human-handsup</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="s" class="mb-4">
<mqtt-button class="btn" width="220" height="130" topic="/sound/playsound" message="bell" rounded>
<v-icon size="100">mdi-bell-outline</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script setup>
import MqttButton from './MqttButton.vue';
</script>

View File

@ -0,0 +1,9 @@
<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>

View File

@ -0,0 +1,23 @@
<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>

View File

@ -0,0 +1,40 @@
<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>

View 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>

View File

@ -0,0 +1,24 @@
<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
src/fetch.txt Normal file
View File

@ -0,0 +1 @@
test

38
src/plugins/router.js Normal file
View File

@ -0,0 +1,38 @@
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

40
src/plugins/vuetify.js Normal file
View File

@ -0,0 +1,40 @@
/**
* 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: {
themes: {
light: {
background: '#212121',
primary: '#cc0000',
controls: '#cc0000',
soundboard: '#9A2779',
secondary: '#b0bec5',
feedback: '#2E7D32',
accent: '#8c9eff',
error: '#b71c1c',
},
dark: {
background: '#121212',
primary: '#2979FF',
controls: '#AB47B',
secondary: '#90a4ae',
feedback: '#2E7D32',
accent: '#8c9eff',
error: '#b71c1c',
},
},
defaultTheme: 'dark',
}
})

View File

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

24
src/views/AboutView.vue Normal file
View File

@ -0,0 +1,24 @@
<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>

42
src/views/GameControl.vue Normal file
View File

@ -0,0 +1,42 @@
<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-container>
<v-row no-gutters>
<v-col>
<card-solution />
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import CardSolution from '@/components/CardSolution.vue'
import CardControl from '@/components/CardControl.vue'
import CardSoundboard from '@/components/CardSoundboard.vue';
</script>
<style>
@media (min-width: 1024px) {
.card__title.primary {
background-color: #2979FF; /* 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: #2979FF; /* Changez la couleur en fonction de votre thème */
}
}
</style>

11
src/views/HomeView.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<v-img width="1645" src="../assets/BrainBlast-For-HomeView-Alpha.png"></v-img>
</template>
<script>
export default {
name: 'VotreComposant', // Assurez-vous de donner un nom à votre composant
// Votre logique JavaScript ici, par exemple des méthodes, des données, etc.
};
</script>

View File

@ -0,0 +1,21 @@
<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>

View File

@ -1,7 +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>

View File

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

View File

@ -1,27 +0,0 @@
<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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

View File

@ -1,10 +0,0 @@
<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>

View File

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

View File

@ -1,34 +0,0 @@
<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>

View File

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

View File

@ -1,46 +0,0 @@
<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>

View File

@ -1,61 +0,0 @@
<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>

View File

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

View File

@ -1,41 +0,0 @@
<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>

View File

@ -1,58 +0,0 @@
<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="40">mdi-console-line</v-icon>
Console MQTT
</v-card-title>
<v-container class="text-center">
<div v-for="(log, index) in messageLogs" :key="index">
<v-label class="v-label-timestamp">{{ log.timestamp }} </v-label> -
<v-label>{{ log.message }}</v-label>
</div>
</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, message: `Topic: ${topic}, Message: ${message}` });
// Limiter la liste à 10 messages
if (this.messageLogs.length > 10) {
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
} }) // S'abonner à tous les topics MQTT
}
}
</script>
<style>
.v-container-style {
display: flex;
justify-content: center;
}
.centered-title {
text-align: center;
}
.v-label-timestamp{
opacity: 100%;
font-weight: 700;
color: #d42828;
}
</style>

View File

@ -1,52 +0,0 @@
<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" height="50" @click="publishMessage">Publier</v-btn>
</v-card>
</v-container>
</template>
<script>
import { publishMessage } from '@/services/mqttService'
export default {
data() { return { message: '', // Initialiser la variable message
selectedTopic: 'topic1',
topics: [ '/display/control', '/sound/playsound', 'topic3', 'topic4', 'topic5', 'topic6', 'topic7', 'topic8', 'topic9', 'topic10' ] // Liste des topics
}
},
methods: {
publishMessage() {
publishMessage(this.selectedTopic, this.message) }
}
}
</script>
<style>
.v-container-style {
align-items: center;
justify-content: center;
}
.input-style{
margin: 20px;
}
.v-btn-style{
align-items: center;
justify-content: center;
width: 100%;
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
border-top-right-radius: 0%;
border-top-left-radius: 0%;
}
.centered-title {
text-align: center;
}
</style>

View File

@ -1,22 +0,0 @@
<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>

View File

@ -1,29 +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 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>

View File

@ -1,16 +0,0 @@
// 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
};

View File

@ -1,33 +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: '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

View File

@ -1,57 +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'
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, },
},
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

View File

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

View File

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

View File

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

View File

@ -1,27 +0,0 @@
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') || '',
};

View File

@ -1,77 +0,0 @@
<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>

View File

@ -1,53 +0,0 @@
<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>

View File

@ -1,3 +0,0 @@
<template>
<v-img src="../assets/BrainBlast-For-HomeView-Alpha.png" class="fill-height"></v-img>
</template>

View File

@ -1,26 +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>
<style>
@media (min-width: 1024px) {
.card__title.primary { background-color: #d42828; /* Changez la couleur en fonction de votre thème */
}
.card__title.feedback { background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
}
.btn{ border-radius:30px!important; background-color: #d42828; /* Changez la couleur en fonction de votre thème */
}
}
</style>

View File

@ -1,132 +0,0 @@
<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>

View File

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