{"id":19858088,"url":"https://github.com/ivanbuccella/camalert","last_synced_at":"2026-04-15T13:31:45.237Z","repository":{"id":111720813,"uuid":"378461342","full_name":"IvanBuccella/CamAlert","owner":"IvanBuccella","description":"CamAlert is an application built using a Serverless Computing approach. The application, through using Nuclio, RabbitMQ, MongoDB, and NodeJS, alerts the user via email of an emergency that is detected from the movement detection alerts which the IoT sensors of the house-installed cameras send into an MQTT queue.","archived":false,"fork":false,"pushed_at":"2022-04-04T16:58:55.000Z","size":173,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-28T23:56:40.703Z","etag":null,"topics":["mqtt","nodejs","serverless"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IvanBuccella.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-19T16:51:47.000Z","updated_at":"2022-04-04T16:58:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"89ddf81e-41e4-4c11-896d-c732ac63c1f9","html_url":"https://github.com/IvanBuccella/CamAlert","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/IvanBuccella/CamAlert","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IvanBuccella%2FCamAlert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IvanBuccella%2FCamAlert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IvanBuccella%2FCamAlert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IvanBuccella%2FCamAlert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IvanBuccella","download_url":"https://codeload.github.com/IvanBuccella/CamAlert/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IvanBuccella%2FCamAlert/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31842856,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T13:28:40.153Z","status":"ssl_error","status_checked_at":"2026-04-15T13:28:29.396Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mqtt","nodejs","serverless"],"created_at":"2024-11-12T14:21:17.850Z","updated_at":"2026-04-15T13:31:45.214Z","avatar_url":"https://github.com/IvanBuccella.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CamAlert\n\nCamAlert is an application built using a Serverless Computing approach. The application, through using Nuclio, RabbitMQ, MongoDB, and NodeJS, alerts the user via email of an emergency that is detected from the movement detection alerts which the IoT sensors of the house-installed cameras send into an MQTT queue.\n\nThe application is mainly composed by:\n\n- MongoDB NoSQL service.\n- Mongo Express service that can be used for managing the MongoDB databases.\n- One serverless Sender Function (used for simulating the sensors) sends a new alert message `{motionBlock: x, cameraID: y,}` value on the MQTT Topic `iot/sensors/cam`.\n- One serverless Consume Function is triggered by a new MQTT message on the Topic `iot/sensors/cam`. It sends a new message `{motionBlock: x, cameraID: y,}` value on the MQTT Topic `iot/logs`.\n- A NodeJS server that logs the invocation of the consume function; this server waits for new messages on the MQTT queue `iot/logs` and it's executed in a dedicated nodeJS service. The server processes and stores the logs into the MongoDB database and, if an emergency is detected, sends an email to the user email address (the env `SENDER_EMAIL_ADDRESS` variable value). The alarm emergency of a camera depends on the `y` number of detections received from a camera in the last `x` seconds (`y` is env `MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS` variable value and `x` is the `MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS` variable value). The sending of an email depends on the emergency detected but, if an email is sent (it will be known because the email logs are stored in the database), the server waits `z` seconds before sending a new email in case of a persistent emergency (`z` is the env `EMAIL_SENDING_TIME_WINDOW_IN_SECONDS` variable value).\n\n![Alt text](assets/structure.jpg?raw=true \"Project Structure\")\n\n#### Tutorial Structure\n\n- **[The Code](#the-code)**\n  - **[Sender Function](#sender-function)**\n  - **[Consume Function](#consume-function)**\n  - **[Server Application](#server-application)**\n    - **[insertAlert function](#insertalert-function)**\n    - **[sendEmail function](#sendemail-function)**\n    - **[isAnEmergency function](#isanemergency-function)**\n- **[Installation](#installation)**\n  - **[Prerequisites](#prerequisites)**\n  - **[Repository](#repository)**\n  - **[Environment Variables](#environment-variables)**\n  - **[Build](#build)**\n  - **[Start Docker Services](#start-docker-services)**\n  - **[Deploy](#deploy)**\n\n## The code\n\n### The serverless functions\n\nEvery function in Nuclio is identified by a serving port, you can see the serving port in the Nuclio dashboard by visiting the URL `http://COMPUTER_IP:NUCLIO_DASHBOARD_PORT` where `COMPUTER_IP = localhost` and `NUCLIO_DASHBOARD_PORT = 8000` are two env variables.\n\n#### Sender Function\n\nThe Sender Function is written in JavaScript and uses the `mqtt` JavaScript library in order to send a new alert message on the queue specified from the `MQTT_QUEUE = iot/sensors/cam` env variable value; the function sends a new message on the topic by following this structure `{motionBlock: x, cameraID: y,}` where `x` and `y` are two random values and respectively identify the `Camera` and the `Block` of the camera visual where the sensors detect the movements.\nThe JavaScript code is the following:\n\n```javascript\nvar mqtt = require(\"mqtt\"),\n  url = require(\"url\");\n\nvar mqtt_url = url.parse(process.env.MQTT_URL);\nvar auth = (mqtt_url.auth || \":\").split(\":\");\nvar url = \"mqtt://\" + mqtt_url.host;\n\nvar options = {\n  port: mqtt_url.port,\n  clientId: \"sender_\" + Math.random().toString(16).substr(2, 8),\n  username: auth[0],\n  password: auth[1],\n};\n\nexports.handler = function (context, event) {\n  var client = mqtt.connect(url, options);\n\n  client.on(\"connect\", function () {\n    let data = {\n      motionBlock: Math.floor(Math.random() * 10),\n      cameraID: Math.floor(Math.random() * 5),\n    };\n    client.publish(process.env.MQTT_QUEUE, JSON.stringify(data), function () {\n      client.end();\n      context.callback(\"MQTT Message Sent\");\n    });\n  });\n};\n```\n\nThe function is deployed using the Docker compose specifics for Nuclio: using a `.yaml` file that defines all functions configurations and the source code.\n\n- The source code (the JavaScript code) is encoded in base64 and copied in the attribute `functionSourceCode` of the `.yaml` file.\n- The Javascript dependencies (libraries) install commands are defined in the `commands` attribute of the `.yaml` file.\n\n```yaml\nmetadata:\n  name: sender\n  labels:\n    nuclio.io/project-name: c4f033ae-fbb7-4649-abf9-f8b75f7c436b\nspec:\n  handler: \"main:handler\"\n  runtime: nodejs\n  env:\n    - name: MQTT_URL\n      value: \"mqtt://guest:guest@10.10.1.1:1883\"\n    - name: MQTT_QUEUE\n      value: iot/sensors/cam\n  resources: {}\n  image: \"nuclio/processor-sender:latest\"\n  minReplicas: 1\n  maxReplicas: 1\n  targetCPU: 75\n  build:\n    image: \"\"\n    noCache: true\n    offline: false\n    dependencies: []\n    runtimeAttributes:\n      repositories: []\n    functionSourceCode: dmFyIG1xdHQgPSByZXF1aXJlKCJtcXR0IiksDQogIHVybCA9IHJlcXVpcmUoInVybCIpOw0KDQp2YXIgbXF0dF91cmwgPSB1cmwucGFyc2UocHJvY2Vzcy5lbnYuTVFUVF9VUkwpOw0KdmFyIGF1dGggPSAobXF0dF91cmwuYXV0aCB8fCAiOiIpLnNwbGl0KCI6Iik7DQp2YXIgdXJsID0gIm1xdHQ6Ly8iICsgbXF0dF91cmwuaG9zdDsNCg0KdmFyIG9wdGlvbnMgPSB7DQogIHBvcnQ6IG1xdHRfdXJsLnBvcnQsDQogIGNsaWVudElkOiAic2VuZGVyXyIgKyBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDE2KS5zdWJzdHIoMiwgOCksDQogIHVzZXJuYW1lOiBhdXRoWzBdLA0KICBwYXNzd29yZDogYXV0aFsxXSwNCn07DQoNCmV4cG9ydHMuaGFuZGxlciA9IGZ1bmN0aW9uIChjb250ZXh0LCBldmVudCkgew0KICB2YXIgY2xpZW50ID0gbXF0dC5jb25uZWN0KHVybCwgb3B0aW9ucyk7DQoNCiAgY2xpZW50Lm9uKCJjb25uZWN0IiwgZnVuY3Rpb24gKCkgew0KICAgIGxldCBkYXRhID0gew0KICAgICAgbW90aW9uQmxvY2s6IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDEwKSwNCiAgICAgIGNhbWVyYUlEOiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiA1KSwNCiAgICB9Ow0KICAgIGNsaWVudC5wdWJsaXNoKHByb2Nlc3MuZW52Lk1RVFRfUVVFVUUsIEpTT04uc3RyaW5naWZ5KGRhdGEpLCBmdW5jdGlvbiAoKSB7DQogICAgICBjbGllbnQuZW5kKCk7DQogICAgICBjb250ZXh0LmNhbGxiYWNrKCJNUVRUIE1lc3NhZ2UgU2VudCIpOw0KICAgIH0pOw0KICB9KTsNCn07DQo=\n    commands:\n      - \"npm install mqtt\"\n      - \"npm install url\"\n    codeEntryType: sourceCode\n  platform: {}\n  readinessTimeoutSeconds: 10\n```\n\n#### Consume Function\n\nThe Consume Function is written in JavaScript and uses the `amqplib` JavaScript library in order to send a new alert message on the queue specified from the `AMQP_QUEUE = iot/logs` env variable value; the invocation of the function is triggered by a new MQTT message on the topic specified from the `MQTT_QUEUE = iot/sensors/cam` env variable value. The JavaScript code is the following:\n\n```javascript\nvar amqp = require(\"amqplib\");\n\nfunction send_feedback(msg) {\n  var q = process.env.AMQP_QUEUE;\n  amqp\n    .connect(process.env.AMQP_URL)\n    .then(function (conn) {\n      return conn\n        .createChannel()\n        .then(function (ch) {\n          var ok = ch.assertQueue(q, { durable: false });\n          return ok.then(function (_qok) {\n            ch.sendToQueue(q, Buffer.from(msg));\n            return ch.close();\n          });\n        })\n        .finally(function () {\n          conn.close();\n        });\n    })\n    .catch(console.warn);\n}\n\nfunction bin2string(array) {\n  var result = \"\";\n  for (var i = 0; i \u003c array.length; ++i) {\n    result += String.fromCharCode(array[i]);\n  }\n  return result;\n}\n\nexports.handler = function (context, event) {\n  var _event = JSON.parse(JSON.stringify(event));\n  var _data = bin2string(_event.body.data);\n\n  context.callback(\"Received \" + _data);\n  send_feedback(_data);\n};\n```\n\nThe function is deployed using the Docker compose specifics for Nuclio: using a `.yaml` file that defines all functions configurations and the source code.\n\n- The source code (the JavaScript code) is encoded in base64 and copied in the attribute `functionSourceCode` of the `.yaml` file.\n- The trigger on the MQTT topic is defined under the `triggers` attribute of the `.yaml` file; it allows to auto-invoke the function on a new message receiving from the topic.\n- The Javascript dependencies (libraries) install commands are defined in the `commands` attribute of the `.yaml` file.\n\n```yaml\nmetadata:\n  name: consumer\n  labels:\n    nuclio.io/project-name: c4f033ae-fbb7-4649-abf9-f8b75f7c436b\nspec:\n  handler: \"main:handler\"\n  runtime: nodejs\n  env:\n    - name: AMQP_URL\n      value: \"amqp://guest:guest@10.10.1.1:5672\"\n    - name: AMQP_QUEUE\n      value: iot/logs\n  resources: {}\n  image: \"nuclio/processor-consumer:latest\"\n  minReplicas: 1\n  maxReplicas: 1\n  targetCPU: 75\n  triggers:\n    mqtt:\n      class: \"\"\n      kind: mqtt\n      url: \"guest:guest@10.10.1.1:1883\"\n      attributes:\n        subscriptions:\n          - qos: 0\n            topic: iot/sensors/cam\n  build:\n    image: \"\"\n    noCache: true\n    offline: true\n    dependencies: []\n    runtimeAttributes:\n      repositories: []\n    functionSourceCode: dmFyIGFtcXAgPSByZXF1aXJlKCJhbXFwbGliIik7DQoNCmZ1bmN0aW9uIHNlbmRfZmVlZGJhY2sobXNnKSB7DQogIHZhciBxID0gcHJvY2Vzcy5lbnYuQU1RUF9RVUVVRTsNCiAgYW1xcA0KICAgIC5jb25uZWN0KHByb2Nlc3MuZW52LkFNUVBfVVJMKQ0KICAgIC50aGVuKGZ1bmN0aW9uIChjb25uKSB7DQogICAgICByZXR1cm4gY29ubg0KICAgICAgICAuY3JlYXRlQ2hhbm5lbCgpDQogICAgICAgIC50aGVuKGZ1bmN0aW9uIChjaCkgew0KICAgICAgICAgIHZhciBvayA9IGNoLmFzc2VydFF1ZXVlKHEsIHsgZHVyYWJsZTogZmFsc2UgfSk7DQogICAgICAgICAgcmV0dXJuIG9rLnRoZW4oZnVuY3Rpb24gKF9xb2spIHsNCiAgICAgICAgICAgIGNoLnNlbmRUb1F1ZXVlKHEsIEJ1ZmZlci5mcm9tKG1zZykpOw0KICAgICAgICAgICAgcmV0dXJuIGNoLmNsb3NlKCk7DQogICAgICAgICAgfSk7DQogICAgICAgIH0pDQogICAgICAgIC5maW5hbGx5KGZ1bmN0aW9uICgpIHsNCiAgICAgICAgICBjb25uLmNsb3NlKCk7DQogICAgICAgIH0pOw0KICAgIH0pDQogICAgLmNhdGNoKGNvbnNvbGUud2Fybik7DQp9DQoNCmZ1bmN0aW9uIGJpbjJzdHJpbmcoYXJyYXkpIHsNCiAgdmFyIHJlc3VsdCA9ICIiOw0KICBmb3IgKHZhciBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgKytpKSB7DQogICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYXJyYXlbaV0pOw0KICB9DQogIHJldHVybiByZXN1bHQ7DQp9DQoNCmV4cG9ydHMuaGFuZGxlciA9IGZ1bmN0aW9uIChjb250ZXh0LCBldmVudCkgew0KICB2YXIgX2V2ZW50ID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShldmVudCkpOw0KICB2YXIgX2RhdGEgPSBiaW4yc3RyaW5nKF9ldmVudC5ib2R5LmRhdGEpOw0KDQogIGNvbnRleHQuY2FsbGJhY2soIlJlY2VpdmVkICIgKyBfZGF0YSk7DQogIHNlbmRfZmVlZGJhY2soX2RhdGEpOw0KfTsNCg==\n    commands:\n      - \"npm install amqplib\"\n    codeEntryType: sourceCode\n  platform: {}\n  readinessTimeoutSeconds: 10\n  timeoutSeconds: 10\n```\n\n### Server Application\n\nThe server application is written in JavaScript and uses the `amqplib, mongodb, and nodemailer` JavaScript libraries in order to receive alert messages on the queue specified from the `AMQP_QUEUE = iot/logs` env variable value, store the alerts, and send an email in case of a detected emergency.\n\nThe server processes and stores the logs into the MongoDB database (by using the `insertAlert` utility function) and, if an emergency is detected, send an email to the `SENDER_EMAIL_ADDRESS`.\n\nThe alarm detection of a camera depends on the `MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS` number of detections received from a camera in the last `MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS` seconds.\n\nThe sending of an email (by using the `sendEmail` utility function) depends on the emergency situation detected (by using the `isAnEmergency` utility function) but, if an email is sent, the server waits `EMAIL_SENDING_TIME_WINDOW_IN_SECONDS` seconds before sending a new email in case of a persistent emergency.\n\nThe JavaScript code - by hiding the utility functions - is the following:\n\n```javascript\nrequire(\"dotenv-expand\")(require(\"dotenv\").config());\nvar amqp = require(\"amqplib\");\nvar nodemailer = require(\"nodemailer\");\nconst { MongoClient } = require(\"mongodb\");\nlet mongoUrl = process.env.DATABASE_URL + \"?authMechanism=DEFAULT\";\nlet mongoOptions = { useUnifiedTopology: true };\n\n.........\nUtility functions\n.........\n\nvar ampq_url = process.env.AMQP_URL;\nvar ampq_queue = process.env.AMQP_QUEUE;\n\namqp\n  .connect(ampq_url)\n  .then(function (conn) {\n    process.once(\"SIGINT\", function () {\n      conn.close();\n    });\n    return conn.createChannel().then(function (ch) {\n      var ok = ch.assertQueue(process.env.AMQP_QUEUE, { durable: false });\n\n      ok = ok.then(function (_qok) {\n        return ch.consume(\n          ampq_queue,\n          async function (msg) {\n            var json = JSON.parse(msg.content);\n            json.date = new Date().toISOString();\n            await insertAlert(json).then(async function (result) {\n              const emergency = await isAnEmergency(json);\n              if (emergency) {\n                await sendEmail(json);\n              }\n            });\n          },\n          { noAck: true }\n        );\n      });\n\n      return ok.then(function (_consumeOk) {\n        console.log(\"Waiting for messages. To exit press CTRL+C\");\n      });\n    });\n  })\n  .catch(console.warn);\n```\n\n#### insertAlert function\n\nThis function uses the `mongodb` JavaScript library for storing an alert identified by the `json` variable and returns the mongodb `document` returned from the `insertOne` method; the alert is stored into the `CamAlert` database and `alerts` collection.\n\nThe JavaScript code is the following:\n\n```javascript\nasync function insertAlert(json) {\n  const client = new MongoClient(mongoUrl, mongoOptions);\n  try {\n    await client.connect();\n    const col = client.db(\"CamAlert\").collection(\"alerts\");\n    const result = await col.insertOne(json);\n    console.log(\n      \"A movement for cam \" +\n        json.cameraID +\n        \" and motion block \" +\n        json.motionBlock +\n        \" has been detected\"\n    );\n    return result;\n  } finally {\n    await client.close();\n  }\n}\n```\n\n#### sendEmail function\n\nThis function uses the `mongodb` JavaScript library for storing the sent emails (into the CamAlert database and alerts collection), and uses the `nodemailer` JavaScript library for sending the emails to the `SENDER_EMAIL_ADDRESS`.\n\nThe JavaScript code is the following:\n\n```javascript\nasync function sendEmail(json) {\n  const client = new MongoClient(mongoUrl, mongoOptions);\n  try {\n    await client.connect();\n    const col = client.db(\"CamAlert\").collection(\"emails\");\n    json.to = process.env.RECIPIENT_EMAIL_ADDRESS;\n    await col.insertOne(json).then(async function () {\n      let transporter = nodemailer.createTransport({\n        host: process.env.SMTP_HOST,\n        port: process.env.SMTP_PORT,\n        secure: process.env.SMTP_SECURE,\n        auth: {\n          user: process.env.SMTP_USER,\n          pass: process.env.SMTP_PASS,\n        },\n      });\n      await transporter.sendMail({\n        from: process.env.SENDER_EMAIL_ADDRESS,\n        to: process.env.RECIPIENT_EMAIL_ADDRESS,\n        subject: \"Movement Detected\",\n        text:\n          \"A movement on cam \" +\n          json.cameraID +\n          \" has been detected in motion block \" +\n          json.motionBlock,\n      });\n    });\n  } finally {\n    await client.close();\n  }\n}\n```\n\n#### isAnEmergency function\n\nThis function uses the `mongodb` JavaScript library for retrieving the alerts (stored into the `CamAlert` database and `alerts` collection) identified by the `json` variable, and returns `true` if the `numberOfAlerts` is greater than `MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS` and the `numberOfEmailsSent` in the last `EMAIL_SENDING_TIME_WINDOW_IN_SECONDS` seconds is zero, `false` otherwise.\n\nThe JavaScript code is the following:\n\n```javascript\nasync function isAnEmergency(json) {\n  const client = new MongoClient(mongoUrl, mongoOptions);\n\n  var movementTimeBreakpoint = new Date();\n  movementTimeBreakpoint.setSeconds(\n    movementTimeBreakpoint.getSeconds() -\n      process.env.MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS\n  ); //MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS seconds backward\n\n  var emailTimeBreakpoint = new Date();\n  emailTimeBreakpoint.setSeconds(\n    emailTimeBreakpoint.getSeconds() -\n      process.env.EMAIL_SENDING_TIME_WINDOW_IN_SECONDS\n  ); //EMAIL_SENDING_TIME_WINDOW_IN_SECONDS seconds backward\n\n  try {\n    await client.connect();\n\n    const numberOfAlerts = await client\n      .db(\"CamAlert\")\n      .collection(\"alerts\")\n      .countDocuments({\n        cameraId: json.cameraId,\n        motionBlock: json.motionBlock,\n        date: { $gte: movementTimeBreakpoint.toISOString() },\n      });\n\n    if (numberOfAlerts \u003c process.env.MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS) {\n      console.log(\n        \"The number of movement detection stored for cam \" +\n          json.cameraID +\n          \" and motion block \" +\n          json.motionBlock +\n          \" in the last \" +\n          process.env.MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS +\n          \" seconds is: \" +\n          numberOfAlerts +\n          \". It is still not considered as an emergency.\"\n      );\n      return false;\n    }\n\n    const numberOfEmailsSent = await client\n      .db(\"CamAlert\")\n      .collection(\"emails\")\n      .countDocuments({\n        cameraId: json.cameraId,\n        motionBlock: json.motionBlock,\n        date: { $gte: emailTimeBreakpoint.toISOString() },\n      });\n    if (numberOfEmailsSent \u003e 0) {\n      console.log(\n        \"The number of movement detection stored for cam \" +\n          json.cameraID +\n          \" and motion block \" +\n          json.motionBlock +\n          \" in the last \" +\n          process.env.MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS +\n          \" seconds is: \" +\n          numberOfAlerts +\n          \". It is considered as an emergency but an email has already been sent in the last \" +\n          process.env.EMAIL_SENDING_TIME_WINDOW_IN_SECONDS +\n          \" seconds.\"\n      );\n      return false;\n    }\n\n    console.log(\n      \"The number of movement detection stored for cam \" +\n        json.cameraID +\n        \" and motion block \" +\n        json.motionBlock +\n        \" in the last \" +\n        process.env.MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS +\n        \" seconds is: \" +\n        numberOfAlerts +\n        \". It is considered as an emergency and an email is sent!!!\"\n    );\n    return true;\n  } finally {\n    await client.close();\n  }\n}\n```\n\n## Installation\n\n### Prerequisites\n\n- Docker and Docker Compose (Application containers engine). Install it from here https://www.docker.com\n- Nuclio (Serverless computing provider)\n- RabbitMQ (AMQP and MQTT message broker)\n- Node.js (if you prefer to execute the Server application without using Docker). Install it from here https://docs.npmjs.com/downloading-and-installing-node-js-and-npm\n\n### Repository\n\nClone the repository:\n\n```sh\n$ git clone https://github.com/IvanBuccella/CamAlert\n```\n\n### Environment Variables\n\nEdit .env file variables by following these instructions:\n\n```\n- COMPUTER_IP: your computer IP address\n- SMTP_HOST: your SMTP server host\n- SMTP_PORT: your SMTP server port\n- SMTP_SECURE: true if your SMTP server uses SSL/TLS, else false\n- SMTP_USER: your SMTP server username\n- SMTP_PASS: your SMTP server password\n- SENDER_EMAIL_ADDRESS: your SMTP server associated \"sender\" email address\n- RECIPIENT_EMAIL_ADDRESS: the email address where you want to receive the alerts\n- MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS: the number of detections to buffer for MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS seconds before sending an email\n- MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS: the number of seconds in which the application look the movement detections backward\n- EMAIL_SENDING_TIME_WINDOW_IN_SECONDS: the number of seconds to wait before sending a new email to the SENDER_EMAIL_ADDRESS address, in case of detection\n```\n\nEdit these environment variables by following these instructions:\n\n- In the `Nuclio/functions/sender.yaml` edit the `MQTT_URL` by replacing the IP with your COMPUTER_IP variable value; e.g. `mqtt://guest:guest@YOUR_COMPUTER_IP_VARIABLE_VALUE:1883`\n- In the `Nuclio/functions/consumer.yaml` edit the `AMQP_URL` by replacing the IP with your COMPUTER_IP variable value; e.g. `amqp://guest:guest@YOUR_COMPUTER_IP_VARIABLE_VALUE:5672`\n- In the `Nuclio/functions/consumer.yaml` edit the `mqtt` trigger `url` by replacing the IP with your COMPUTER_IP variable value; e.g. `guest:guest@YOUR_COMPUTER_IP_VARIABLE_VALUE:1883`\n\n### Build\n\nIf you prefer to execute the `server application` without using Docker, you need to remove the server service from the docker-compose.yml file:\n\n```yml\nserver:\n  build:\n    context: ./Server\n    dockerfile: Dockerfile.dev\n    args:\n      - version=${NODE_VERSION}\n  volumes:\n    - \"./Server/code/server.js:/usr/app/server.js\"\n  environment:\n    - AMQP_URL=${AMQP_URL}\n    - AMQP_QUEUE=${AMQP_QUEUE}\n    - DATABASE_URL=${DATABASE_URL}\n    - SMTP_HOST=${SMTP_HOST}\n    - SMTP_PORT=${SMTP_PORT}\n    - SMTP_SECURE=${SMTP_SECURE}\n    - SMTP_USER=${SMTP_USER}\n    - SMTP_PASS=${SMTP_PASS}\n    - RECIPIENT_EMAIL_ADDRESS=${RECIPIENT_EMAIL_ADDRESS}\n    - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS}\n    - MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS=${MOVEMENT_DETECTION_TIME_WINDOW_IN_SECONDS}\n    - MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS=${MINIMUM_NUMBER_OF_MOVEMENT_DETECTIONS}\n    - EMAIL_SENDING_TIME_WINDOW_IN_SECONDS=${EMAIL_SENDING_TIME_WINDOW_IN_SECONDS}\n  depends_on:\n    - \"rabbitmq\"\n    - \"nuclio\"\n    - \"database\"\n  restart: always\n```\n\nBuild the local environment with Docker:\n\n```sh\n$ docker-compose build\n```\n\n### Start Docker Services\n\n```sh\n$ docker-compose up\n```\n\n### Deploy\n\nVisit the Nuclio Dashboard by typing `http://COMPUTER_IP:NUCLIO_DASHBOARD_PORT` where `COMPUTER_IP = localhost` and `NUCLIO_DASHBOARD_PORT = 8000` are two env variables, and create a project named `CamAlert`. Then:\n\n- Create and deploy the Consumer function into the `CamAlert` project by using the YAML file stored in the `Nuclio/functions/consumer.yaml` path.\n- Create and deploy the Sender function into the `CamAlert` project by using the YAML file stored in the `Nuclio/functions/sender.yaml` path.\n\nIf you prefer to execute the `server application` without using Docker, you need to:\n\n- copy `.env` file into the server code folder:\n\n```sh\ncp .env Server/code/.env\n```\n\n- move into the server code folder:\n\n```sh\ncd Server/code\n```\n\n- install the dependencies:\n\n```sh\nnpm install amqplib dotenv dotenv-expand mongodb nodemailer\n```\n\n- execute the server:\n\n```sh\nnode server.js\n```\n\n### Enjoy :-)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanbuccella%2Fcamalert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivanbuccella%2Fcamalert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanbuccella%2Fcamalert/lists"}