diff --git a/README.md b/README.md index 7fad88a..6eb91dd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ # Temperature Alarm +![Picture of the BeagleBone](docs/img/beaglebone_small.jpg) + +![Screenshot of the dashboard](docs/img/dashboard_small.png) + +## Setup + +In the `backend/Api` folder, copy `appsettings.example.json` into `appsettings.json` and fill out the values. + +In the `frontend` folder, copy `shared/constants.example.js` into `shared/constants.js` and fill out the values. + +In the `iot` folder, copy `config.example.h` into `config.h` and fill out the values. + +## Running + +To run the backend, type `dotnet run` within the `backend/Api` folder. Make sure you have the .NET 8 SDK installed. + +The frontend should just be hosted with a static web server. To run locally, you may e.g. use PHP: `php -S 0.0.0.0:80` + +The IoT code should be run on a BeagleBone Black with: + +- A Grove Base Cape, set to 5V +- A JHD1313 Grove-LCD RGB Backlight display in one of the I2C\_2 ports +- An MCP9808 Grove High Accuracy Temperature Sensor in one of the I2C\_2 ports +- A Grove Buzzer in the GPIO\_51 port + +The following library packages are required by the IoT code and should be installed on the BeagleBone: + +`apt install libmosquitto-dev librabbitmq-dev libi2c-dev libgpiod-dev libcjson-dev` + +> [!NOTE] +> The gpiod library must be below version 2.0. Debian Bookworm still distributes the old version of the library. + +> [!NOTE] +> The RabbitMQ apt package is missing the header files - They can be copied from [here](https://github.com/alanxz/rabbitmq-c/tree/master/include/rabbitmq-c) into the `/usr/include/rabbitmq-c` folder + +Copy the source files onto the BeagleBone and run the following command to compile and run the code: + +`make && ./a.out` + +To run in the background, a program such as GNU Screen can be used. + diff --git a/backend/Api/AMQP/AMQPReciever.cs b/backend/Api/AMQP/AMQPReciever.cs index e9811c6..9a8eb5c 100644 --- a/backend/Api/AMQP/AMQPReciever.cs +++ b/backend/Api/AMQP/AMQPReciever.cs @@ -41,7 +41,7 @@ namespace Api.AMQPReciever var messageReceive = JsonSerializer.Deserialize(message); // Checks if the message has the data we need - if (messageReceive == null || messageReceive.device_id == null || messageReceive.timestamp == 0) + if (messageReceive == null || messageReceive.device_id == null || messageReceive.timestamp == 0 || messageReceive.temperature > 200.0) { return Task.CompletedTask; } diff --git a/backend/Api/appsettings.example.json b/backend/Api/appsettings.example.json new file mode 100644 index 0000000..c3763aa --- /dev/null +++ b/backend/Api/appsettings.example.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "ConnectionStrings": { + "Database": "Data Source=database.sqlite3" + }, + + "JwtSettings": { + "Issuer": "TemperatureAlarmApi", + "Audience": "Customers", + "Key": "" + } +} + diff --git a/backend/test/Program.cs b/backend/test/Program.cs deleted file mode 100644 index cc6d76a..0000000 --- a/backend/test/Program.cs +++ /dev/null @@ -1,36 +0,0 @@ -using MQTTnet; - -var mqttFactory = new MqttClientFactory(); -IMqttClient mqttClient; - -using (mqttClient = mqttFactory.CreateMqttClient()) - { - var mqttClientOptions = new MqttClientOptionsBuilder() - .WithTcpServer($"10.135.51.116", 1883) - .WithCredentials($"h5", $"Merc1234") - .WithCleanSession() - .Build(); - - // Setup message handling before connecting so that queued messages - // are also handled properly. When there is no event handler attached all - // received messages get lost. - mqttClient.ApplicationMessageReceivedAsync += e => - { - Console.WriteLine("Received application message."); - - return Task.CompletedTask; - }; - - - await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); - Console.WriteLine("mqttClient"); - - //var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(topic).Build(); - - await mqttClient.SubscribeAsync("temperature"); - - Console.WriteLine("MQTT client subscribed to topic."); - - Console.WriteLine("Press enter to exit."); - Console.ReadLine(); -} diff --git a/backend/test/test.csproj b/backend/test/test.csproj deleted file mode 100644 index f105f42..0000000 --- a/backend/test/test.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - diff --git a/docs/img/beaglebone.jpg b/docs/img/beaglebone.jpg new file mode 100644 index 0000000..3ed5005 Binary files /dev/null and b/docs/img/beaglebone.jpg differ diff --git a/docs/img/beaglebone_small.jpg b/docs/img/beaglebone_small.jpg new file mode 100644 index 0000000..530eace Binary files /dev/null and b/docs/img/beaglebone_small.jpg differ diff --git a/docs/img/dashboard.png b/docs/img/dashboard.png new file mode 100644 index 0000000..5d1f242 Binary files /dev/null and b/docs/img/dashboard.png differ diff --git a/docs/img/dashboard_small.png b/docs/img/dashboard_small.png new file mode 100644 index 0000000..5f0d6d5 Binary files /dev/null and b/docs/img/dashboard_small.png differ diff --git a/frontend/profile/index.html b/frontend/profile/index.html index 9c740e4..1ad1402 100644 --- a/frontend/profile/index.html +++ b/frontend/profile/index.html @@ -4,6 +4,7 @@ Register - Temperature Alarm + @@ -67,7 +68,7 @@ -
+
diff --git a/frontend/scripts/login.js b/frontend/scripts/login.js index 0323fc6..becf53e 100644 --- a/frontend/scripts/login.js +++ b/frontend/scripts/login.js @@ -10,14 +10,15 @@ document.getElementById("loginForm").addEventListener("submit", function(event) login(emailOrUsername, password) .then(response => { - document.cookie = `auth-token=${response.token}; Path=/`; - document.cookie = `refresh-token=${response.refreshToken}; Path=/`; - localStorage.setItem("user", JSON.stringify({ - id: response.id, - username: response.userName, - })); - - location.href = "/home"; + if(response.token && response.refreshToken){ + document.cookie = `auth-token=${response.token}; Path=/`; + document.cookie = `refresh-token=${response.refreshToken}; Path=/`; + localStorage.setItem("user", JSON.stringify({ + id: response.id, + username: response.userName, + })); + location.href = "/home"; + } }) .catch(error => { document.getElementById("form-error").innerText = error; diff --git a/frontend/scripts/profile.js b/frontend/scripts/profile.js index 1f824d6..47724dd 100644 --- a/frontend/scripts/profile.js +++ b/frontend/scripts/profile.js @@ -8,10 +8,10 @@ var username; var email; get().then((res) => { - username = res.userName; - email = res.email; - var table = document.getElementById(`profileCard`); - table.innerHTML += ` + username = res.userName; + email = res.email; + var table = document.getElementById(`profileCard`); + table.innerHTML += `
@@ -43,79 +43,76 @@ usernameInput.addEventListener("input", checkForChanges); // Open modals editIconbtn.onclick = () => { - document.getElementById("uname").value = username; - document.getElementById("email").value = email; - submitBtn.disabled = true; - editModal.style.display = "block"; + document.getElementById("uname").value = username; + document.getElementById("email").value = email; + submitBtn.disabled = true; + editModal.style.display = "block"; }; passwordBtn.onclick = () => (pswModal.style.display = "block"); // Close modals when clicking on any close button document.querySelectorAll(".close").forEach((closeBtn) => { - closeBtn.onclick = () => { - pswModal.style.display = "none"; - editModal.style.display = "none"; - document.getElementById("form-error").innerText = ""; - document.getElementById("form-error").style.display = "none"; - }; + closeBtn.onclick = () => { + pswModal.style.display = "none"; + editModal.style.display = "none"; + document.getElementById("form-error").innerText = ""; + document.getElementById("form-error").style.display = "none"; + }; }); // Close modals when clicking outside window.onclick = (event) => { - if (event.target == pswModal || event.target == editModal) { - pswModal.style.display = "none"; - editModal.style.display = "none"; - document.getElementById("form-error").innerText = ""; - document.getElementById("form-error").style.display = "none"; - } + if (event.target == pswModal || event.target == editModal) { + pswModal.style.display = "none"; + editModal.style.display = "none"; + document.getElementById("form-error").innerText = ""; + document.getElementById("form-error").style.display = "none"; + } }; document - .getElementById("editForm") - .addEventListener("submit", function (event) { - event.preventDefault(); // Prevents default form submission + .getElementById("editForm") + .addEventListener("submit", function (event) { + event.preventDefault(); // Prevents default form submission - document.getElementById("form-error").style.display = "none"; + document.getElementById("form-error").style.display = "none"; - // Call function with form values - update(emailInput.value, usernameInput.value).then((response) => { - if (response?.error) { - document.getElementById("form-error").innerText = response.error; - document.getElementById("form-error").style.display = "block"; + // Call function with form values + update(emailInput.value, usernameInput.value).then((response) => { + if (response?.error) { + document.getElementById("form-error").innerText = response.error; + document.getElementById("form-error").style.display = "block"; - return; - } + return; + } - location.href = "/profile"; + location.href = "/profile"; + }); }); - }); document - .getElementById("PasswordForm") - .addEventListener("submit", function (event) { - event.preventDefault(); // Prevents default form submission + .getElementById("PasswordForm") + .addEventListener("submit", function (event) { + event.preventDefault(); // Prevents default form submission - document.getElementById("form-error").style.display = "none"; + document.getElementById("password-error").style.display = "none"; - const oldPassword = document.getElementById("oldpsw").value; - const newPassword = document.getElementById("psw").value; - const repeatPassword = document.getElementById("rpsw").value; + const oldPassword = document.getElementById("oldpsw").value; + const newPassword = document.getElementById("psw").value; + const repeatPassword = document.getElementById("rpsw").value; - if (newPassword !== repeatPassword) { - let errorDiv = document.getElementById("form-error"); - errorDiv.style.display = "block"; - errorDiv.innerText = "Passwords do not match!"; - return; - } + if (newPassword !== repeatPassword) { + document.getElementById("password-error").style.display = "block"; + document.getElementById("password-error").innerText = "Passwords do not match!"; + return; + } - updatePassword(oldPassword, newPassword).then((response) => { - //error messages do not work - if (response.error) { - document.getElementById("form-error").innerText = response.message; - document.getElementById("form-error").style.display = "block"; - return; - } + updatePassword(oldPassword, newPassword) + .then(() => location.reload()) + .catch(error => { + document.getElementById("password-error").innerText = error; + document.getElementById("password-error").style.display = "block"; + }); }); - }); document.querySelector(".logout-container").addEventListener("click", logout); diff --git a/frontend/scripts/services/devices.service.js b/frontend/scripts/services/devices.service.js index bc756a6..a2fc77a 100644 --- a/frontend/scripts/services/devices.service.js +++ b/frontend/scripts/services/devices.service.js @@ -5,7 +5,7 @@ export function getDevices() { } export function add(referenceId) { - return request("POST", `/device/adddevice/${referenceId}`); + return request("POST", `/device/add/${referenceId}`); } export function deleteDevice(deviceId) { diff --git a/frontend/scripts/services/users.service.js b/frontend/scripts/services/users.service.js index c228c2c..ad5c7a9 100644 --- a/frontend/scripts/services/users.service.js +++ b/frontend/scripts/services/users.service.js @@ -39,7 +39,7 @@ export function update(email, username){ } export function updatePassword(oldPassword, newPassword){ - return request("PUT", "/user/update-password", { + return request("PUT", "/user/change-password", { oldPassword, newPassword, }); diff --git a/frontend/shared/utils.js b/frontend/shared/utils.js index e04c46d..c0c94c0 100644 --- a/frontend/shared/utils.js +++ b/frontend/shared/utils.js @@ -41,36 +41,36 @@ export async function request(method, path, body = null) { export function checkTokens() { var token = document.cookie.match(/\bauth-token=([^;\s]+)/); - if(token != null){ - return token[1] + if (token != null) { + return token[1]; } + const match = document.cookie.match(/\brefresh-token=([^;\s]+)/); token = match ? match[1] : null; - console.log("refresh "+token); - if(token != null){ - return fetch(`${address}/user/refreshtoken/${token}`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - }) - .then(async response => { - if (!response.ok) { - window.location.href = "/login"; - return; - } - - const json = await response.json() - - document.cookie = `auth-token=${json.token}; Path=/`; - document.cookie = `refresh-token=${json.refreshToken}; Path=/`; - - return json.token; - }); - } - else{ - window.location.href = "/login"; - } + + if (token != null) { + return fetch(`${address}/user/refreshtoken/${token}`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + }) + .then(async response => { + if (!response.ok) { + location.href = "/login"; + return; + } + + const json = await response.json() + + document.cookie = `auth-token=${json.token}; Path=/`; + document.cookie = `refresh-token=${json.refreshToken}; Path=/`; + + return json.token; + }); + } + + location.href = "/login"; } export function logout() { diff --git a/iot/main.c b/iot/main.c index 1b509a4..e8cf4cb 100644 --- a/iot/main.c +++ b/iot/main.c @@ -78,6 +78,7 @@ void *watch_temperature(void *arg) // Sound alarm if applicable if ( + temperature < 200.0 && min_temperature != NAN && max_temperature != NAN && (temperature < min_temperature || temperature > max_temperature) ) {