Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions server/mock-socket-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ io.on('connection', (socket) => {
socket.emit('sensorData', { temperature: initialData.temperature, humidity: initialData.humidity });
socket.emit('powerStatus', { powerCut: initialData.powerCut });

// Send initial system startup log
socket.emit('systemLog', {
type: 'success',
message: 'System initialized successfully',
source: 'system',
timestamp: new Date()
});

// intervals object to clear later
const intervals = {};

Expand Down
3 changes: 1 addition & 2 deletions src/components/EnergyCharts.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ const EnergyCharts = () => {

return (
<div className="space-y-4 sm:space-y-6">

{/* Real-time Charts */}
{/* Real-time Energy Consumption */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
Expand Down
23 changes: 21 additions & 2 deletions src/components/StatusCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { motion } from 'framer-motion';
import { useApp } from '../context/AppContext';

const StatusCard = ({ title, value, subtitle, icon, color, onClick }) => {
const StatusCard = ({ title, value: propValue, subtitle: propSubtitle, icon, color: propColor, onClick }) => {
const { state } = useApp();

const getStatusColor = () => {
Expand Down Expand Up @@ -38,6 +38,11 @@ const StatusCard = ({ title, value, subtitle, icon, color, onClick }) => {

const warningLevel = getWarningLevel();

// Build derived values without mutating props
let value = propValue;
let subtitle = propSubtitle;
let color = propColor || 'status-offline';

if (title === 'Inverter Status') {
value = `${getStatusIcon()} ${state.inverterStatus.toUpperCase()}`;
color = getStatusColor();
Expand Down Expand Up @@ -77,11 +82,25 @@ const StatusCard = ({ title, value, subtitle, icon, color, onClick }) => {
}
}

if (title === 'Energy Usage') {
value = `${state.energyConsumption}W`;
if (state.energyConsumption > 500) {
subtitle = 'High consumption';
color = 'status-warning-battery';
} else if (state.energyConsumption > 300) {
subtitle = 'Moderate consumption';
color = 'status-standby';
} else {
subtitle = 'Low consumption';
color = 'status-online';
}
}

return (
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className={`rounded-xl p-6 text-white shadow-lg cursor-pointer transition-all duration-300 ${color} status-card`}
className={`rounded-xl p-4 sm:p-6 text-white shadow-lg cursor-pointer transition-all duration-300 interactive ${color}`}
onClick={onClick}
role="button"
tabIndex={0}
Expand Down
13 changes: 13 additions & 0 deletions src/context/AppContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const initialState = {
isManualMode: false,
currentTime: new Date().toLocaleTimeString(),
theme: 'light',
logs: [],
};

function appReducer(state, action) {
Expand Down Expand Up @@ -47,6 +48,18 @@ function appReducer(state, action) {
return { ...state, currentTime: action.payload };
case 'SET_THEME':
return { ...state, theme: action.payload };
case 'ADD_LOG':
const newLog = {
id: Date.now() + Math.random(),
...action.payload,
timestamp: action.payload.timestamp || new Date()
};
return {
...state,
logs: [newLog, ...state.logs].slice(0, 1000) // Keep max 1000 logs
};
case 'CLEAR_LOGS':
return { ...state, logs: [] };
default:
return state;
}
Expand Down
77 changes: 77 additions & 0 deletions src/context/SocketContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ export function SocketProvider({ children }) {
message: 'Connected to real-time server',
timestamp: new Date(),
});
dispatch({
type: 'ADD_LOG',
payload: {
type: 'success',
message: 'Connected to real-time server',
source: 'system'
}
});
});

newSocket.on('disconnect', () => {
Expand Down Expand Up @@ -89,10 +97,28 @@ export function SocketProvider({ children }) {

newSocket.on('inverterStatus', (data) => {
dispatch({ type: 'UPDATE_INVERTER_STATUS', payload: data.status });
dispatch({
type: 'ADD_LOG',
payload: {
type: 'info',
message: `Inverter status changed to ${data.status}`,
source: 'inverter'
}
});
});

newSocket.on('powerStatus', (data) => {
dispatch({ type: 'UPDATE_POWER_STATUS', payload: data.powerCut });
const logType = data.powerCut ? 'warning' : 'success';
const logMessage = data.powerCut ? 'Power cut detected! Running on inverter' : 'Grid power restored';
dispatch({
type: 'ADD_LOG',
payload: {
type: logType,
message: logMessage,
source: 'power'
}
});
if (data.powerCut) {
addNotification({
type: 'warning',
Expand All @@ -104,14 +130,65 @@ export function SocketProvider({ children }) {

newSocket.on('batteryUpdate', (data) => {
dispatch({ type: 'UPDATE_BATTERY_LEVEL', payload: data.level });
let logType = 'info';
let logMessage = `Battery level updated to ${data.level}%`;

if (data.level <= 20) {
logType = 'error';
logMessage = `Critical battery level: ${data.level}%`;
} else if (data.level <= 30) {
logType = 'warning';
logMessage = `Low battery warning: ${data.level}%`;
}

dispatch({
type: 'ADD_LOG',
payload: {
type: logType,
message: logMessage,
source: 'battery'
}
});
});

newSocket.on('energyUpdate', (data) => {
dispatch({ type: 'UPDATE_ENERGY_DATA', payload: data.consumption });
dispatch({
type: 'ADD_LOG',
payload: {
type: 'info',
message: `Energy consumption: ${data.consumption}W`,
source: 'energy'
}
});
});

newSocket.on('sensorData', (data) => {
dispatch({ type: 'UPDATE_SENSOR_DATA', payload: data });
let logType = 'info';
let logMessage = `Temperature: ${data.temperature}°C, Humidity: ${data.humidity}%`;

if (data.temperature > 60) {
logType = 'error';
logMessage = `Critical temperature warning: ${data.temperature}°C`;
} else if (data.temperature > 50) {
logType = 'warning';
logMessage = `High temperature: ${data.temperature}°C`;
}

dispatch({
type: 'ADD_LOG',
payload: {
type: logType,
message: logMessage,
source: 'sensor'
}
});
});

// Listen for direct log events from server
newSocket.on('systemLog', (logData) => {
dispatch({ type: 'ADD_LOG', payload: logData });
});

// Surface server-side notifications and command acknowledgements
Expand Down
70 changes: 7 additions & 63 deletions src/pages/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,34 +133,10 @@ const Dashboard = () => {
role="region"
aria-label="System status cards"
>
<StatusCard
title="Inverter Status"
value="Online"
subtitle="All systems normal"
icon="🔌"
color="status-online"
/>
<StatusCard
title="Power Supply"
value="Grid Active"
subtitle="Stable power supply"
icon="⚡"
color="status-online"
/>
<StatusCard
title="Battery Level"
value="75%"
subtitle="Adequate charge"
icon="🔋"
color="status-online"
/>
<StatusCard
title="Temperature"
value="45°C"
subtitle="Normal temperature"
icon="🌡️"
color="status-online"
/>
<StatusCard title="Inverter Status" icon="🔌" />
<StatusCard title="Power Supply" icon="⚡" />
<StatusCard title="Battery Level" icon="🔋" />
<StatusCard title="Temperature" icon="🌡️" />
</motion.div>

{/* Energy Usage Card - Separate row for better mobile layout */}
Expand All @@ -172,41 +148,9 @@ const Dashboard = () => {
role="region"
aria-label="Energy usage information"
>
<StatusCard
title="Energy Usage"
value="320W"
subtitle="Moderate consumption"
icon="📊"
color="status-standby"
/>
</motion.div>

{/* Utility Functions Display Cards */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"
>
<StatusCard
title="Estimated Backup Time"
value={backupTime}
subtitle={`At ${currentConsumption}W consumption`}
icon="⏱️"
color="status-online"
/>
<StatusCard
title="System Efficiency"
value={`${efficiencyData.percentage}%`}
subtitle={`Rating: ${efficiencyData.rating}`}
icon="⚡"
color={
efficiencyData.color === "green"
? "status-online"
: efficiencyData.color === "yellow"
? "status-warning"
: "status-error"
}
<StatusCard
title="Energy Usage"
icon="📊"
/>
</motion.div>

Expand Down
Loading
Loading