From 321e09cd5b1ac116175e6ead57d5082bdf523201 Mon Sep 17 00:00:00 2001 From: Bhaskar Date: Tue, 28 Jan 2025 14:19:31 +0530 Subject: [PATCH 1/3] notification chnages --- src/controllers/tanksController.js | 820 ++++++++++++++++++++++------- 1 file changed, 644 insertions(+), 176 deletions(-) diff --git a/src/controllers/tanksController.js b/src/controllers/tanksController.js index 09bbee2a..2a3fe73e 100644 --- a/src/controllers/tanksController.js +++ b/src/controllers/tanksController.js @@ -1758,39 +1758,39 @@ eventEmitter.on( // }); // Event listener to handle notification -eventEmitter.on('sendLowWaterNotification', async (fcmToken, tankInfo) => { - const message = formatWaterLevelMessage(tankInfo, 'low'); - sendNotification(fcmToken, message); -}); +// eventEmitter.on('sendLowWaterNotification', async (fcmToken, tankInfo) => { +// const message = formatWaterLevelMessage(tankInfo, 'low'); +// sendNotification(fcmToken, message); +// }); -eventEmitter.on('sendVeryLowWaterNotification', async (fcmToken, tankInfo) => { - const message = formatWaterLevelMessage(tankInfo, 'very low'); - sendNotification(fcmToken, message); -}); +// eventEmitter.on('sendVeryLowWaterNotification', async (fcmToken, tankInfo) => { +// const message = formatWaterLevelMessage(tankInfo, 'very low'); +// sendNotification(fcmToken, message); +// }); -eventEmitter.on('sendCriticalLowWaterNotification', async (fcmToken, tankInfo) => { - const message = formatWaterLevelMessage(tankInfo, 'critically low'); - sendNotification(fcmToken, message); -}); +// eventEmitter.on('sendCriticalLowWaterNotification', async (fcmToken, tankInfo) => { +// const message = formatWaterLevelMessage(tankInfo, 'critically low'); +// sendNotification(fcmToken, message); +// }); -const formatWaterLevelMessage = (tankInfo, levelType) => { - const tankName = tankInfo.tankName; - const tankLocation = tankInfo.tankLocation; - const waterLevel = parseInt(tankInfo.waterlevel, 10); - const capacity = parseInt(tankInfo.capacity, 10); - const volumeInLitres = (capacity * waterLevel) / 100; // assuming the capacity is in litres - - let levelDescription = ''; - if (levelType === 'low') { - levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; - } else if (levelType === 'very low') { - levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; - } else if (levelType === 'critically low') { - levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; - } +// const formatWaterLevelMessage = (tankInfo, levelType) => { +// const tankName = tankInfo.tankName; +// const tankLocation = tankInfo.tankLocation; +// const waterLevel = parseInt(tankInfo.waterlevel, 10); +// const capacity = parseInt(tankInfo.capacity, 10); +// const volumeInLitres = (capacity * waterLevel) / 100; // assuming the capacity is in litres + +// let levelDescription = ''; +// if (levelType === 'low') { +// levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; +// } else if (levelType === 'very low') { +// levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; +// } else if (levelType === 'critically low') { +// levelDescription = `${waterLevel}% (${volumeInLitres.toFixed(2)} L)`; +// } - return `Water level in '${tankName}', located at '${tankLocation}', type of water: ${tankInfo.waterType} is ${levelType} at ${levelDescription}. Action: start motor now.`; -}; +// return `Water level in '${tankName}', located at '${tankLocation}', type of water: ${tankInfo.waterType} is ${levelType} at ${levelDescription}. Action: start motor now.`; +// }; // Emit low water level event with motorId // eventEmitter.on('lowWaterLevel', async (fcmTokens, timestamp, motorId, waterLevel) => { @@ -1827,52 +1827,201 @@ eventEmitter.on('sendThresholdTimeNotification', async (fcmTokens, message) => { } }); +// eventEmitter.on( +// 'sendMotorStartNotification', +// async (fcmTokens, motorId, waterLevel, blockName, tankName, motorOnType, stopCriteria, manual_threshold_time) => { +// try { +// // Get the latest timestamp +// const startTime = new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + +// // Retrieve the user information +// const users = await User.find({ fcmIds: { $in: fcmTokens } }); +// const userNames = users.map(user => user.username).join(', '); +// const startMethod = motorOnType.toUpperCase() === "Forced Manual"; + +// // Prepare the message +// const message = +// `🚰 Tank Name: '${tankName}'\n` + +// `🕒 Pump started at: '${startTime}'\n` + +// `👤 Initiated by: ${userNames}\n` + +// `🔄 Pump started by: '${startMethod}'`; + +// // Send the notification +// await sendNotification(fcmTokens, 'Motor Started 🚀', message); +// console.log('Motor start notification sent successfully!'); +// } catch (error) { +// console.error('Error in sendMotorStartNotification event:', error); +// } +// } +// ); + eventEmitter.on( 'sendMotorStartNotification', - async (fcmTokens, motorId, waterLevel, blockName, tankName, motorOnType, stopCriteria, manual_threshold_time) => { + async ( + fcmTokens, + blockName, + tankName, + motorOnType, + stopCriteria, + typeOfWater, + highThreshold // Add the high threshold as a parameter + ) => { try { // Get the latest timestamp - const startTime = new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + const currentDateTime = new Date(); + const startTime = currentDateTime.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + const formattedDate = currentDateTime.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' }); + const formattedTime = currentDateTime.toLocaleTimeString('en-IN', { timeZone: 'Asia/Kolkata' }); // Retrieve the user information const users = await User.find({ fcmIds: { $in: fcmTokens } }); const userNames = users.map(user => user.username).join(', '); - const startMethod = motorOnType.toUpperCase() === "Forced Manual"; - // Prepare the message + // Determine the pump initiation method + const startMethod = motorOnType === "Forced Manual" ? "Physically" : "Manually" + + // Dynamically generate the motor name + const motorName = `${tankName}-${blockName}-${typeOfWater}`; + + // Determine the stop condition message + let stopConditionMessage = ""; + if (stopCriteria === "manual") { + stopConditionMessage = `Will stop at Manually \n`; + } else if (stopCriteria === "highThreshold") { + stopConditionMessage = `🚨 Pump will stop when the water level reaches the high threshold of ${highThreshold}%.\n`; + } + + // Prepare the notification message const message = + `🚰 Motor Name: ${motorName}\n` + `🚰 Tank Name: '${tankName}'\n` + - `🕒 Pump started at: '${startTime}'\n` + - `👤 Initiated by: ${userNames}\n` + - `🔄 Pump started by: '${startMethod}'`; + `🏢 Block Name: '${blockName}'\n` + + `👤 Started by: ${userNames}\n` + + `📱 Mode: '${startMethod}'\n` + + `🕒 Pump started at: ${startTime} (Time: ${formattedTime} on ${formattedDate})\n` + + stopConditionMessage; // Add only the relevant stop condition // Send the notification await sendNotification(fcmTokens, 'Motor Started 🚀', message); - console.log('Motor start notification sent successfully!'); + console.log('Motor start notification with stop criteria sent successfully!'); } catch (error) { console.error('Error in sendMotorStartNotification event:', error); } } ); + +// eventEmitter.on( +// 'sendMotorStartNotification', +// async (fcmTokens, motorId, waterLevel, blockName, tankName, motorOnType, stopCriteria, manual_threshold_time, typeOfWater) => { +// try { +// // Get the latest timestamp +// const currentDateTime = new Date(); +// const startTime = currentDateTime.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); +// const formattedDate = currentDateTime.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' }); +// const formattedTime = currentDateTime.toLocaleTimeString('en-IN', { timeZone: 'Asia/Kolkata' }); + +// // Retrieve the user information +// const users = await User.find({ fcmIds: { $in: fcmTokens } }); +// const userNames = users.map(user => user.username).join(', '); + +// // Determine the pump initiation method +// const startMethod = motorOnType === "Mobile APP" ? "Mobile APP" : "Manual"; + +// // Dynamically generate the motor name +// const motorName = `${tankName}-${blockName}-${typeOfWater}`; + +// // Prepare the notification message +// const message = +// `🚰 Motor Name: '${motorName}'\n` + +// `🚰 Tank Name: '${tankName}'\n` + +// `🏢 Block Name: '${blockName}'\n` + +// `💧 Water Level: '${waterLevel}%'\n` + +// `👤 Initiated by: ${userNames}\n` + +// `📱 Pump started by: '${startMethod}'\n` + +// `🕒 Start Time: '${startTime}'\n` + +// `⏳ Will stop after: '${manual_threshold_time}' mins\n` + +// `📅 Date: '${formattedDate}'\n` + +// `⏰ Time: '${formattedTime}'`; + +// // Send the notification +// await sendNotification(fcmTokens, 'Motor Started 🚀', message); +// console.log('Motor start notification sent successfully!'); +// } catch (error) { +// console.error('Error in sendMotorStartNotification event:', error); +// } +// } +// ); + + +// eventEmitter.on( +// 'sendMotorStopNotification', +// async (fcmTokens, motorId, waterLevel, blockName, tankName, motorOnType) => { +// try { +// // Get the latest timestamp +// const stopTime = new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + +// // Retrieve the user information +// const users = await User.find({ fcmIds: { $in: fcmTokens } }); +// const userNames = users.map(user => user.username).join(', '); +// const stopMethod = motorOnType.toUpperCase() === "Forced Manual"; + +// // Prepare the message +// const message = +// `🚰 Tank Name: '${tankName}'\n` + +// `🕒 Pump stopped at: '${stopTime}'\n` + +// `👤 Initiated by: ${userNames}\n` + +// `🔄 Pump stopped by: '${stopMethod}'\n`; + +// // Send the notification +// await sendNotification(fcmTokens, 'Motor Stopped 🛑', message); +// console.log('Motor stop notification sent successfully!'); +// } catch (error) { +// console.error('Error in sendMotorStopNotification event:', error); +// } +// } +// ); + eventEmitter.on( 'sendMotorStopNotification', - async (fcmTokens, motorId, waterLevel, blockName, tankName, motorOnType) => { + async ( + fcmTokens, + blockName, + tankName, + motorOnType, + typeOfWater + ) => { try { // Get the latest timestamp - const stopTime = new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + const currentDateTime = new Date(); + const stopTime = currentDateTime.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' }); + const formattedDate = currentDateTime.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' }); + const formattedTime = currentDateTime.toLocaleTimeString('en-IN', { timeZone: 'Asia/Kolkata' }); // Retrieve the user information const users = await User.find({ fcmIds: { $in: fcmTokens } }); const userNames = users.map(user => user.username).join(', '); - const stopMethod = motorOnType.toUpperCase() === "Forced Manual"; - // Prepare the message + // Determine the pump stop method + const stopMethod = motorOnType === "Forced Manual" ? "Physically" : "Manually"; + + // Dynamically generate the motor name + const motorName = `${tankName}-${blockName}-${typeOfWater}`; + // Determine the stop condition message + let stopConditionMessage = ""; + if (stopCriteria === "manual") { + stopConditionMessage = `Stopped at Manually \n`; + } else if (stopCriteria === "highThreshold") { + stopConditionMessage = `🚨 Pump will stop when the water level reaches the high threshold of ${highThreshold}%.\n`; + } + // Prepare the notification message const message = + `🚰 Motor Name: ${motorName}\n` + `🚰 Tank Name: '${tankName}'\n` + - `🕒 Pump stopped at: '${stopTime}'\n` + - `👤 Initiated by: ${userNames}\n` + - `🔄 Pump stopped by: '${stopMethod}'\n`; + `🏢 Block Name: '${blockName}'\n` + + `📱 Mode: '${stopMethod}'\n` + + `🕒 Pump stopped at: ${stopTime} (Time: ${formattedTime} on ${formattedDate})\n` + + stopConditionMessage; // Send the notification await sendNotification(fcmTokens, 'Motor Stopped 🛑', message); @@ -1883,6 +2032,31 @@ eventEmitter.on( } ); +eventEmitter.on('sendLowWaterNotification', (fcmTokens, message) => { + const notificationMessage = `Warning: Water level is low in the tank. + Tank Name: ${tankName}, + Location: ${receiverTank.location}, + Type of Water: ${receiverTank.typeOfWater}, + Current Water Level: ${currentWaterLevel} liters (${currentWaterPercentage.toFixed(2)}%), + Date & Time: ${new Date().toLocaleString()}`; + + // Send notifications using the provided FCM tokens + sendNotification(fcmTokens, notificationMessage); +}); + +eventEmitter.on('sendCriticalLowWaterNotification', (fcmTokens, message) => { + const notificationMessage = `Critical Alert: Water level is critically low in the tank. + Tank Name: ${tankName}, + Location: ${receiverTank.location}, + Type of Water: ${receiverTank.typeOfWater}, + Current Water Level: ${currentWaterLevel} liters (${currentWaterPercentage.toFixed(2)}%), + Date & Time: ${new Date().toLocaleString()}`; + + // Send notifications using the provided FCM tokens + sendNotification(fcmTokens, notificationMessage); +}); + + // eventEmitter.on('sendMotorStartNotification', async (fcmTokens, message) => { // try { @@ -1956,12 +2130,21 @@ const sendNotification = async (fcmIds, title, body) => { notification: { title, body }, token, data: { - 'target': '/route_navigation', + 'target': '/screen', }, }); console.log(`Notification sent successfully to token: ${token}`, response); } catch (error) { console.error(`Failed to send notification to token: ${token}`, error); + // Check for specific error indicating an invalid token + if (error.code === 'messaging/registration-token-not-registered') { + // Remove the invalid token from the database + await User.updateOne( + { fcmIds: token }, // Ensure you're targeting the right user with the invalid token + { $pull: { fcmIds: token } } // Remove the invalid token + ); + console.log(`Removed invalid token: ${token}`); + } } }); @@ -2313,71 +2496,90 @@ const notificationSentStatus = { criticallyHighWater: false, }; -const checkWaterLevelsAndNotify = async (customerId, tankName, tankLocation, fcmTokens) => { - try { - // Fetch the tank details from the database - const tank = await Tank.findOne({ customerId, tankName, tankLocation }); +let waterLevelCheckInterval; // To hold the interval ID - if (!tank) { - console.error(`Tank not found: ${tankName} at location ${tankLocation}`); - return; - } +const checkWaterLevel = async (customerId, motorId, fcmToken, receiverTank) => { + const currentWaterLevel = parseInt(receiverTank.waterlevel, 10); + const tankCapacity = parseInt(receiverTank.capacity.replace(/,/g, ''), 10); + const currentWaterPercentage = (currentWaterLevel / tankCapacity) * 100; - // Extract the current water level and capacity - const currentWaterLevel = parseInt(tank.waterlevel, 10); - const capacity = parseInt(tank.capacity.replace(/,/g, ''), 10); - - // Calculate the water level percentage - const waterLevelPercentage = (currentWaterLevel / capacity) * 100; - - // Thresholds for notifications - const thresholds = { - criticallyLow: 10, - veryLow: 20, - low: 30, - high: 70, - veryHigh: 80, - criticallyHigh: 85, - }; + // Check for low water levels + if (currentWaterPercentage <= 20 && !notificationSentStatus.lowWater) { + eventEmitter.emit('sendLowWaterNotification', fcmToken, `Water level has dropped below 20%.`); + notificationSentStatus.lowWater = true; + } - // Check water levels and send notifications - if (waterLevelPercentage <= thresholds.criticallyLow && !notificationSentStatus.criticallyLowWater) { - eventEmitter.emit('sendCriticalLowWaterNotification', fcmTokens, tank); - notificationSentStatus.criticallyLowWater = true; - await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCritical: true } }); - } else if (waterLevelPercentage <= thresholds.veryLow && !notificationSentStatus.veryLowWater) { - eventEmitter.emit('sendVeryLowWaterNotification', fcmTokens, tank); - notificationSentStatus.veryLowWater = true; - await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryLow: true } }); - } else if (waterLevelPercentage <= thresholds.low && !notificationSentStatus.lowWater) { - eventEmitter.emit('sendLowWaterNotification', fcmTokens, tank); - notificationSentStatus.lowWater = true; - await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentLow: true } }); - } - // if (waterLevelPercentage <= thresholds.criticallyLow) { - // eventEmitter.emit('sendCriticalLowWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCritical: true } }); - // } else if (waterLevelPercentage <= thresholds.veryLow) { - // eventEmitter.emit('sendVeryLowWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryLow: true } }); - // } else if (waterLevelPercentage <= thresholds.low) { - // eventEmitter.emit('sendLowWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentLow: true } }); - // } - // else if (waterLevelPercentage >= thresholds.criticallyHigh) { - // eventEmitter.emit('sendCriticalHighWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCriticalHigh: true } }); - // } else if (waterLevelPercentage >= thresholds.veryHigh) { - // eventEmitter.emit('sendVeryHighWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryHigh: true } }); - // } else if (waterLevelPercentage >= thresholds.high) { - // eventEmitter.emit('sendHighWaterNotification', fcmTokens, tank); - // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentHigh: true } }); - // } - } catch (error) { - console.error(`Error checking water levels for tank ${tankName}:`, error); + // Check for critically low water levels + if (currentWaterPercentage <= 10 && !notificationSentStatus.criticallyLowWater) { + eventEmitter.emit('sendCriticalLowWaterNotification', fcmToken, `Water level has dropped below 10%.`); + notificationSentStatus.criticallyLowWater = true; } }; +// const checkWaterLevelsAndNotify = async (customerId, tankName, tankLocation, fcmTokens) => { +// try { +// // Fetch the tank details from the database +// const tank = await Tank.findOne({ customerId, tankName, tankLocation }); + +// if (!tank) { +// console.error(`Tank not found: ${tankName} at location ${tankLocation}`); +// return; +// } + +// // Extract the current water level and capacity +// const currentWaterLevel = parseInt(tank.waterlevel, 10); +// const capacity = parseInt(tank.capacity.replace(/,/g, ''), 10); + +// // Calculate the water level percentage +// const waterLevelPercentage = (currentWaterLevel / capacity) * 100; + +// // Thresholds for notifications +// const thresholds = { +// criticallyLow: 10, +// veryLow: 20, +// low: 30, +// high: 70, +// veryHigh: 80, +// criticallyHigh: 85, +// }; + +// // Check water levels and send notifications +// if (waterLevelPercentage <= thresholds.criticallyLow && !notificationSentStatus.criticallyLowWater) { +// eventEmitter.emit('sendCriticalLowWaterNotification', fcmTokens, tank); +// notificationSentStatus.criticallyLowWater = true; +// await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCritical: true } }); +// } else if (waterLevelPercentage <= thresholds.veryLow && !notificationSentStatus.veryLowWater) { +// eventEmitter.emit('sendVeryLowWaterNotification', fcmTokens, tank); +// notificationSentStatus.veryLowWater = true; +// await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryLow: true } }); +// } else if (waterLevelPercentage <= thresholds.low && !notificationSentStatus.lowWater) { +// eventEmitter.emit('sendLowWaterNotification', fcmTokens, tank); +// notificationSentStatus.lowWater = true; +// await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentLow: true } }); +// } +// // if (waterLevelPercentage <= thresholds.criticallyLow) { +// // eventEmitter.emit('sendCriticalLowWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCritical: true } }); +// // } else if (waterLevelPercentage <= thresholds.veryLow) { +// // eventEmitter.emit('sendVeryLowWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryLow: true } }); +// // } else if (waterLevelPercentage <= thresholds.low) { +// // eventEmitter.emit('sendLowWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentLow: true } }); +// // } +// // else if (waterLevelPercentage >= thresholds.criticallyHigh) { +// // eventEmitter.emit('sendCriticalHighWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentCriticalHigh: true } }); +// // } else if (waterLevelPercentage >= thresholds.veryHigh) { +// // eventEmitter.emit('sendVeryHighWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentVeryHigh: true } }); +// // } else if (waterLevelPercentage >= thresholds.high) { +// // eventEmitter.emit('sendHighWaterNotification', fcmTokens, tank); +// // await Tank.updateOne({ customerId, tankName: tank.tankName }, { $set: { notificationSentHigh: true } }); +// // } +// } catch (error) { +// console.error(`Error checking water levels for tank ${tankName}:`, error); +// } +// }; const monitorWaterLevels = async () => { @@ -2479,7 +2681,7 @@ exports.motorAction = async (req, reply) => { ? `${req.body.manual_threshold_time} minutes` : `${req.body.manual_threshold_litres} litres`; - await checkWaterLevelsAndNotify(customerId, tankName, receiverTank.tankLocation, fcmToken); + // await checkWaterLevelsAndNotify(customerId, tankName, receiverTank.tankLocation, fcmToken); if (!notificationSentStatus.motorStart) { eventEmitter.emit( @@ -2494,6 +2696,12 @@ exports.motorAction = async (req, reply) => { ); notificationSentStatus.motorStart = true; // Set flag to true to prevent duplicate notifications + } + // Start checking water level every 30 minutes + if (!waterLevelCheckInterval) { + waterLevelCheckInterval = setInterval(async () => { + await checkWaterLevel(customerId, motorId, fcmToken, receiverTank); + }, 30 * 60 * 1000); // 30 minutes } await Tank.updateOne( { customerId, "connections.inputConnections.motor_id": motorId }, @@ -2507,30 +2715,8 @@ exports.motorAction = async (req, reply) => { } else if (action === "stop") { motorStopStatus = "1"; // If action is stop, set stop status to "1" - - - await checkWaterLevelsAndNotify(customerId, tankName, receiverTank.tankLocation, fcmToken); - - // eventEmitter.emit( - // "motorStop", - // fcmToken, - // // motorId, - // // currentWaterLevel, - // // blockName, - // tankName, - // stopTime, - // motorOnType - // ); - if (!notificationSentStatus.motorStop) { - // eventEmitter.emit( - // "motorStop", - // fcmToken, - // tankName, - // stopTime, - // "APP" - // ); // Emit motorStop event const totalWaterPumped = await calculateTotalPumpedWater(customerId, motorId, start_instance_id); // A function to calculate total water pumped @@ -2554,7 +2740,11 @@ exports.motorAction = async (req, reply) => { "connections.inputConnections.$.motor_on_type": motorOnType } } ); - + // Clear the interval when the motor is stopped + if (waterLevelCheckInterval) { + clearInterval(waterLevelCheckInterval); + waterLevelCheckInterval = null; // Reset the interval ID + } } else { throw new Error("Invalid action provided."); } @@ -2695,7 +2885,7 @@ exports.motorAction = async (req, reply) => { // clearInterval(motorIntervals[motorId]); // Clear interval // delete motorIntervals[motorId]; - await checkWaterLevelsAndNotify(customerId, tankName, supplierTank.tankLocation, fcmToken); + // await checkWaterLevelsAndNotify(customerId, tankName, supplierTank.tankLocation, fcmToken); // if (currentWaterPercentage >= highWaterThreshold && !notificationSentStatus.highWater) { // eventEmitter.emit('sendHighWaterNotification', fcmToken, `Water level has reached high levels.`); // notificationSentStatus.highWater = true; // Set flag to true to prevent duplicate notifications @@ -5237,6 +5427,7 @@ exports.getBlockData = async (req, reply) => { const mqtt = require('mqtt'); +const { setMaxIdleHTTPParsers } = require('http'); const client = mqtt.connect('mqtt://35.207.198.4:1883'); // Connect to MQTT broker client.on('connect', () => { @@ -5353,17 +5544,43 @@ client.on('message', async (topic, message) => { inputConnection.motor_on_type = "forced_manual"; inputConnection.startTime = currentTime; // Emit motor start notification with tankName - eventEmitter.emit( - "sendMotorStartNotification", - fcmToken, // FCM tokens - hw_Id, // Motor ID - inputConnection.water_level || 0, // Water level - motorTank.blockName || "N/A", // Block name - tankName, // Tank name - inputConnection.motor_on_type, // Motor on type - "threshold", // Stop criteria - manual_threshold_time // Threshold time in mins - ); + // eventEmitter.emit( + // "sendMotorStartNotification", + // fcmToken, // FCM tokens + // hw_Id, // Motor ID + // inputConnection.water_level || 0, // Water level + // motorTank.blockName || "N/A", // Block name + // tankName, // Tank name + // inputConnection.motor_on_type, // Motor on type + // "threshold", // Stop criteria + // manual_threshold_time // Threshold time in mins + // ); + + // Define logic to determine stopCriteria + let stopCriteria = ""; + let highThreshold = 90; // Example high threshold value + + let stopConditionMessage = ""; + if (stopCriteria === "manual") { + stopConditionMessage = `Will stop at Manually \n`; + } else if (stopCriteria === "highThreshold") { + stopConditionMessage = `🚨 Pump will stop when the water level reaches the high threshold of ${highThreshold}%.\n`; + } + + // Emit the event + try { + eventEmitter.emit("sendMotorStartNotification", + fcmToken, + motorTank.blockName || "N/A", + motorTank.tankName, + "Forced Manual", + stopCriteria, + motorTank.typeOfWater || "Drinking Water", + highThreshold + ); + } catch (error) { + console.error('Error emitting motor start notification:', error); + } } @@ -5371,18 +5588,20 @@ client.on('message', async (topic, message) => { inputConnection.motor_stop_status = "1"; // Emit motor stop notification with tankName - eventEmitter.emit( - "sendMotorStopNotification", - fcmToken, // FCM tokens - hw_Id, // Motor ID - inputConnection.water_level || 0, // Water level - motorTank.blockName || "N/A", // Block name - tankName, // Tank name - inputConnection.motor_on_type // Motor on type - ); + try { + eventEmitter.emit("sendMotorStopNotification", + fcmToken, + motorTank.blockName || "N/A", + motorTank.tankName, + "Forced Manual", + motorTank.typeOfWater || "Drinking Water" + ); + } catch (error) { + console.error('Error emitting motor stop notification:', error); + } } - await motorTank.save(); // Save the updated tank + await motorTank.save(); } console.log('Data processed successfully for hardwareId:', hw_Id); // Updated variable name @@ -5629,13 +5848,160 @@ console.log("this is for testing autopush,line located in tankscontroller") +// const calculateDailyConsumptionAndNotify = async () => { +// try { +// const today = moment().startOf("day"); +// const yesterday = moment(today).subtract(1, "days"); + +// // Fetch all active users +// const activeUsers = await User.find({ }); + +// for (const user of activeUsers) { +// const { customerId, fcmIds } = user; + +// // Fetch daily consumption for the customer +// const consumptions = await TankConsumptionOriginalSchema.find({ +// customerId, +// time: { +// $gte: yesterday.format("DD-MMM-YYYY - HH:mm"), +// $lt: today.format("DD-MMM-YYYY - HH:mm"), +// }, +// }); + +// // Calculate total consumption +// const totalConsumption = consumptions.reduce((total, record) => { +// return total + parseInt(record.consumption, 10); +// }, 0); + +// // Prepare tank-wise consumption details +// const tankDetails = consumptions.map((record) => ({ +// tankName: record.tankName, +// consumption: record.consumption, +// })); + +// // Send notification +// const notificationTitle = "Daily Water Consumption Report"; +// const notificationBody = ` +// Total Consumption: ${totalConsumption} liters +// Tank Details: ${tankDetails +// .map((tank) => `${tank.tankName}: ${tank.consumption} liters`) +// .join(", ")} +// `; + +// if (fcmIds && fcmIds.length > 0) { +// await sendNotification(fcmIds, notificationTitle, notificationBody); +// } +// } + +// console.log("Daily consumption notifications sent successfully."); +// } catch (err) { +// console.error("Error sending daily consumption notifications:", err); +// } +// }; + + +// cron.schedule("0 11:57 * * *", async () => { +// console.log("Starting daily consumption notification task..."); +// await calculateDailyConsumptionAndNotify(); +// }); + +// cron.schedule( +// "0 9 * * *", +// async () => { +// console.log("Starting daily consumption notification task..."); +// await calculateDailyConsumptionAndNotify(); +// }, +// { +// timezone: "Asia/Kolkata", // Specify the timezone +// } +// ); + +// const calculateDailyConsumptionAndNotify = async () => { +// try { +// const today = moment().startOf("day"); +// const yesterday = moment(today).subtract(1, "days"); + +// // Fetch all active users +// const activeUsers = await User.find({}); + +// for (const user of activeUsers) { +// const { customerId, fcmIds } = user; + +// // Fetch daily consumption for the customer +// const consumptions = await TankConsumptionOriginalSchema.find({ +// customerId, +// time: { +// $gte: yesterday.format("DD-MMM-YYYY - HH:mm"), +// $lt: today.format("DD-MMM-YYYY - HH:mm"), +// }, +// }); + +// // Calculate total consumption by type of water and the water level percentage +// const consumptionSummary = consumptions.reduce((acc, record) => { +// const typeOfWater = record.typeOfWater; // Assuming this field exists +// const consumption = parseInt(record.consumption, 10); +// const waterLevel = parseInt(record.waterLevel, 10); // Assuming waterLevel is in percentage + +// if (!acc[typeOfWater]) { +// acc[typeOfWater] = { +// totalConsumption: 0, +// tankDetails: [], +// totalWaterLevel: 0, +// count: 0, +// }; +// } + +// acc[typeOfWater].totalConsumption += consumption; +// acc[typeOfWater].totalWaterLevel += waterLevel; +// acc[typeOfWater].count += 1; +// acc[typeOfWater].tankDetails.push({ +// tankName: record.tankName, +// consumption, +// waterLevel, +// }); + +// return acc; +// }, {}); + +// // Prepare notification body +// let notificationBody = "Daily Water Consumption Report:\n"; +// for (const type in consumptionSummary) { +// const { totalConsumption, tankDetails, totalWaterLevel, count } = consumptionSummary[type]; +// const averageWaterLevel = (totalWaterLevel / count).toFixed(2); // Calculate average water level +// console.log("averageWaterLevel",averageWaterLevel) +// console.log("totalConsumption",totalConsumption) + +// notificationBody += ` +// Type of Water: ${type} +// Total Consumption: ${totalConsumption} liters +// Average Water Level: ${averageWaterLevel}% +// `; +// console.log("noti---" ,notificationBody += ` +// Type of Water: ${type} +// Total Consumption: ${totalConsumption} liters +// Average Water Level: ${averageWaterLevel}% +// `) +// } + +// if (fcmIds && fcmIds.length > 0) { +// await sendNotification(fcmIds, "Daily Water Consumption Report", notificationBody); +// } +// } + +// console.log("Daily consumption notifications sent successfully."); +// } catch (err) { +// console.error("Error sending daily consumption notifications:", err); +// } +// }; + + const calculateDailyConsumptionAndNotify = async () => { try { const today = moment().startOf("day"); const yesterday = moment(today).subtract(1, "days"); // Fetch all active users - const activeUsers = await User.find({ }); + const activeUsers = await User.find({}); for (const user of activeUsers) { const { customerId, fcmIds } = user; @@ -5649,28 +6015,46 @@ const calculateDailyConsumptionAndNotify = async () => { }, }); - // Calculate total consumption - const totalConsumption = consumptions.reduce((total, record) => { - return total + parseInt(record.consumption, 10); - }, 0); + // Calculate total consumption and capacities based on water type + let totalBoreConsumption = 0; + let totalDrinkingConsumption = 0; + let totalBoreCapacity = 0; + let totalDrinkingCapacity = 0; + + for (const record of consumptions) { + const typeOfWater = record.typeOfWater; // Assuming this field exists + const consumption = parseInt(record.consumption, 10); + const capacity = parseInt(record.capacity, 10); // Assuming capacity field exists + + if (typeOfWater === "bore" || typeOfWater === "Bore Water") { + totalBoreConsumption += consumption; + totalBoreCapacity += capacity; + } else if (typeOfWater === "drinking" || typeOfWater === "Drinking Water") { + totalDrinkingConsumption += consumption; + totalDrinkingCapacity += capacity; + } + } - // Prepare tank-wise consumption details - const tankDetails = consumptions.map((record) => ({ - tankName: record.tankName, - consumption: record.consumption, - })); + // Calculate percentages + const boreConsumptionPercentage = totalBoreCapacity + ? ((totalBoreConsumption / totalBoreCapacity) * 100).toFixed(2) + : 0; + + const drinkingConsumptionPercentage = totalDrinkingCapacity + ? ((totalDrinkingConsumption / totalDrinkingCapacity) * 100).toFixed(2) + : 0; - // Send notification - const notificationTitle = "Daily Water Consumption Report"; - const notificationBody = ` - Total Consumption: ${totalConsumption} liters - Tank Details: ${tankDetails - .map((tank) => `${tank.tankName}: ${tank.consumption} liters`) - .join(", ")} - `; + // Prepare notification body + const reportDate = yesterday.format("DD-MMM-YYYY"); + let notificationBody = `Daily Water Consumption Report for ${reportDate}:\n`; + notificationBody += `Total Bore Consumption: ${totalBoreConsumption} liters\n`; + notificationBody += `Bore Water Consumption Percentage: ${boreConsumptionPercentage}%\n`; + notificationBody += `Total Drinking Consumption: ${totalDrinkingConsumption} liters\n`; + notificationBody += `Drinking Water Consumption Percentage: ${drinkingConsumptionPercentage}%\n`; + // Send notification if FCM IDs are present if (fcmIds && fcmIds.length > 0) { - await sendNotification(fcmIds, notificationTitle, notificationBody); + await sendNotification(fcmIds, "Daily Water Consumption Report", notificationBody); } } @@ -5680,12 +6064,7 @@ const calculateDailyConsumptionAndNotify = async () => { } }; - -// cron.schedule("0 11:57 * * *", async () => { -// console.log("Starting daily consumption notification task..."); -// await calculateDailyConsumptionAndNotify(); -// }); - +// Schedule the cron job to run daily at 9 AM cron.schedule( "0 9 * * *", async () => { @@ -5696,3 +6075,92 @@ cron.schedule( timezone: "Asia/Kolkata", // Specify the timezone } ); + + +const calculateConsumptionAndNotify = async () => { + try { + const now = moment(); // Current time + const sixHoursAgo = moment(now).subtract(6, 'hours').startOf('hour'); // 6 hours ago + + // Fetch all active users + const activeUsers = await User.find({}); + + for (const user of activeUsers) { + const { customerId, fcmIds } = user; + + // Fetch consumption records for the last 6 hours + const consumptions = await TankConsumptionOriginalSchema.find({ + customerId, + time: { + $gte: sixHoursAgo.format("DD-MMM-YYYY - HH:mm"), + $lt: now.format("DD-MMM-YYYY - HH:mm"), + }, + }); + + // Prepare notification body + let notificationBody = `Water Consumption Report (From ${sixHoursAgo.format( + "hh:mm A" + )} to ${now.format("hh:mm A")}):\n`; + const tankDetails = {}; + + // Aggregate consumption data by tank + for (const record of consumptions) { + const tankName = record.tankName; // Assuming this field exists + const tankLocation = record.tankLocation; // Assuming this field exists + const consumption = parseInt(record.consumption, 10); // Liters consumed + const typeOfWater = record.typeOfWater; // Type of water (e.g., bore, drinking) + const tankCapacity = parseInt(record.capacity, 10); // Tank capacity in liters + + if (!tankDetails[tankName]) { + tankDetails[tankName] = { + tankLocation, + totalConsumption: 0, + typeOfWater, + tankCapacity, + }; + } + tankDetails[tankName].totalConsumption += consumption; + } + + // Format tank details for the notification + for (const tankName in tankDetails) { + const { + tankLocation, + totalConsumption, + typeOfWater, + tankCapacity, + } = tankDetails[tankName]; + const consumptionPercentage = tankCapacity + ? ((totalConsumption / tankCapacity) * 100).toFixed(2) + : 0; + + notificationBody += + `Tank Name: ${tankName} \n`+ + `Location: ${tankLocation} \n`+ + `Total Consumption: ${totalConsumption} liters ${consumptionPercentage}% \n`+ + `Type of Water: ${typeOfWater}`; + } + + // Send notification if FCM IDs are present + if (fcmIds && fcmIds.length > 0) { + await sendNotification(fcmIds, "Water Consumption Report", notificationBody); + } + } + + console.log("Consumption notifications sent successfully."); + } catch (err) { + console.error("Error sending consumption notifications:", err); + } +}; + +// Schedule notifications at 6 AM, 12 PM, 6 PM, and 12 AM +cron.schedule( + "0 6,12,18,0 * * *", // Cron expression for the required times + async () => { + console.log("Starting scheduled consumption notification task..."); + await calculateConsumptionAndNotify(); + }, + { + timezone: "Asia/Kolkata", // Specify the timezone + } +); From 7d6c1a5544b4492d5a3268ec7f76ab7f51abb71f Mon Sep 17 00:00:00 2001 From: Bhaskar Date: Tue, 28 Jan 2025 14:31:34 +0530 Subject: [PATCH 2/3] locations list in admin --- src/controllers/departmentController.js | 11 +++++++++++ src/routes/departmentRoute.js | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/controllers/departmentController.js b/src/controllers/departmentController.js index b3302718..87c40348 100644 --- a/src/controllers/departmentController.js +++ b/src/controllers/departmentController.js @@ -446,6 +446,17 @@ exports.addDepartment = async (request, reply) => { throw boom.boomify(err); } }; + + exports.getallLocationData = async (req, reply) => { + try { + console.log("Fetching all zones..."); // Debug log + const locations = await Deparments.distinct('location'); + reply.send({ status_code: 200, data: locations, count: locations.length }); + } catch (err) { + console.error("Error fetching cities:", err); // Log the error for debugging + throw boom.boomify(err); + } + }; exports.deletedepartmentInfo = async (req, reply) => { try { diff --git a/src/routes/departmentRoute.js b/src/routes/departmentRoute.js index 5b51b0fd..78328a01 100644 --- a/src/routes/departmentRoute.js +++ b/src/routes/departmentRoute.js @@ -266,6 +266,23 @@ module.exports = function (fastify, opts, next) { //preHandler: fastify.auth([fastify.authenticate]), handler: departmentController.getallZonesData, }); + + fastify.get("/api/getalllocationsdata", { + schema: { + tags: ["Department"], + description: "This is for Get all Locations Data", + summary: "This is for to Get all Locations Data", + + security: [ + { + basicAuth: [], + }, + ], + }, + //preHandler: fastify.auth([fastify.authenticate]), + handler: departmentController.getallLocationData, + }); + fastify.delete("/api/deletedepartment/:departmentId", { schema: { description: "Delete a Department by departmentId", From 600b0a00eaf6c9b1bbc118536da250c902914fa8 Mon Sep 17 00:00:00 2001 From: Bhaskar Date: Tue, 28 Jan 2025 14:50:43 +0530 Subject: [PATCH 3/3] cities and zones and locations in admin --- src/controllers/departmentController.js | 38 ++++++++++++++++++------- src/routes/departmentRoute.js | 16 +++++------ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/controllers/departmentController.js b/src/controllers/departmentController.js index 87c40348..a24b7235 100644 --- a/src/controllers/departmentController.js +++ b/src/controllers/departmentController.js @@ -427,37 +427,53 @@ exports.addDepartment = async (request, reply) => { exports.getallCitiesData = async (req, reply) => { try { console.log("Fetching all cities..."); // Debug log - const cities = await Deparments.distinct('city'); // Use distinct to get unique cities - console.log("Cities fetched:", cities); // Log the fetched cities - reply.send({ status_code: 200, data: cities, count: cities.length }); + const cities = await City.distinct('city'); // Fetch distinct city names from the database + + // Normalize the city names to avoid duplicates + const normalizedCities = [...new Set(cities.map(city => city.trim().toUpperCase()))]; + + console.log("Cities fetched:", normalizedCities); // Log the cleaned cities + reply.send({ status_code: 200, data: normalizedCities, count: normalizedCities.length }); } catch (err) { console.error("Error fetching cities:", err); // Log the error for debugging throw boom.boomify(err); } }; + exports.getallZonesData = async (req, reply) => { try { console.log("Fetching all zones..."); // Debug log - const zones = await Deparments.distinct('zone'); - reply.send({ status_code: 200, data: zones, count: zones.length }); + const zones = await City.distinct('zone'); // Fetch distinct zone names from the database + + // Normalize the zone names to avoid duplicates + const normalizedZones = [...new Set(zones.map(zone => zone.trim().toUpperCase()))]; + + console.log("Zones fetched:", normalizedZones); // Log the cleaned zones + reply.send({ status_code: 200, data: normalizedZones, count: normalizedZones.length }); } catch (err) { - console.error("Error fetching cities:", err); // Log the error for debugging + console.error("Error fetching zones:", err); // Log the error for debugging throw boom.boomify(err); } }; - + exports.getallLocationData = async (req, reply) => { try { - console.log("Fetching all zones..."); // Debug log - const locations = await Deparments.distinct('location'); - reply.send({ status_code: 200, data: locations, count: locations.length }); + console.log("Fetching all locations..."); // Debug log + const locations = await City.distinct('location'); // Fetch distinct locations from the database + + // Normalize the location names to uppercase and remove duplicates + const normalizedLocations = [...new Set(locations.map(location => location.trim().toUpperCase()))]; + + console.log("Locations fetched:", normalizedLocations); // Log the cleaned locations + reply.send({ status_code: 200, data: normalizedLocations, count: normalizedLocations.length }); } catch (err) { - console.error("Error fetching cities:", err); // Log the error for debugging + console.error("Error fetching locations:", err); // Log the error for debugging throw boom.boomify(err); } }; + exports.deletedepartmentInfo = async (req, reply) => { try { const departmentId = req.params.departmentId; diff --git a/src/routes/departmentRoute.js b/src/routes/departmentRoute.js index 78328a01..9ebc96a7 100644 --- a/src/routes/departmentRoute.js +++ b/src/routes/departmentRoute.js @@ -157,8 +157,8 @@ module.exports = function (fastify, opts, next) { url: "/api/departmentSignup", schema: { tags: ["Department"], - description: "This is for creating a new Department Account", - summary: "This is for creating a new Department Account", + description: "This is for creating a new Team Member Account", + summary: "This is for creating a new Ream Member Account", body: { type: "object", //required: ["phone", "username", "password", "role"], // Add role to required fields @@ -239,8 +239,8 @@ module.exports = function (fastify, opts, next) { fastify.get("/api/getallcitiesdata", { schema: { tags: ["Department"], - description: "This is for Get all Cities Data", - summary: "This is for to Get all Cities Data", + description: "This is for Get all Cities Data for City Schema", + summary: "This is for to Get all Cities Data for City Schema", security: [ { @@ -254,8 +254,8 @@ module.exports = function (fastify, opts, next) { fastify.get("/api/getallzonesdata", { schema: { tags: ["Department"], - description: "This is for Get all Zones Data", - summary: "This is for to Get all Zones Data", + description: "This is for Get all Zones Data for City Schema", + summary: "This is for to Get all Zones Data for City Schema", security: [ { @@ -270,8 +270,8 @@ module.exports = function (fastify, opts, next) { fastify.get("/api/getalllocationsdata", { schema: { tags: ["Department"], - description: "This is for Get all Locations Data", - summary: "This is for to Get all Locations Data", + description: "This is for Get all Locations Data for City Schema", + summary: "This is for to Get all Locations Data for City Schema", security: [ {