-
Notifications
You must be signed in to change notification settings - Fork 0
Frontend Documentation
Warning
This page is legacy / archived and may be outdated. It is preserved for historical context only and is not the source of truth for current operations.
Use current documentation instead:
You can also start from Archive / Legacy Index.
-
- 12.1. getTelemetryTypes
- 12.2. getTelemetryData
- 12.3. getAllChargers
- 12.4. getFavorites
- 12.5. toggleFavorite
- 12.6. getCombinedChargerData
- 12.7. getAnomalies
- 12.8. addAnomaly
- 12.9. deleteAnomaly
- 12.10. syncChargers
- 12.11. syncTelemetry
- 12.12. syncTelemetryShort
- 12.13. loadCpuUsage
- 12.14. loadCpuThermal
- 12.15. loadMonitoring
Returntype: string
Description: This is a function to format the Timestamps which are given from the Datapoints for CPU Usage and CPU Thermal for the Linecharts to show in the X Axis. The function filters the Timestamp and sets it up in the format day:month, hour:minute:second
Code:
const formatDateMultiline = (value: string) => {
const date = new Date(value);
const day = String(date.getDate()).padStart(2, "0");
const month = String(date.getMonth() + 1).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
const second = String(date.getSeconds()).padStart(2, "0");
return `${day}.${month}, ${hour}:${minute}:${second}`;
};Returntype: number
Description: Filters The Timestamp and matches it with the input from the user. Its the function for the Datepicker from and to date and the time. The same is done with "const filteredDataCpuThermal".
Code:
const filteredDataCpuUsage = controllerCpuUsageData.filter((cpu) => {
const t = new Date(cpu.timestamp).getTime();
const f = fromDateUsage?.getTime() ?? -Infinity;
const u = toDateUsage?.getTime() ?? Infinity;
return t >= f && t <= u;
});1.3. handleLastMinutes(dataArray:CPU[], setFrom: Date|undefined, setTo: Date|undefined, minutes: number):
Description: This function takes the data from dataArray and the sets 2 react states "from" and "to". First it calculates the maxTime and o this basis it calculates the minTime. With those 2 it sets a Timespan in which the matching Datapoints will be shown.
Code:
function handleLastMinutes(
dataArray: Cpu[],
setFrom: React.Dispatch<React.SetStateAction<Date | undefined>>,
setTo: React.Dispatch<React.SetStateAction<Date | undefined>>,
minutes: number
) {
if (dataArray.length === 0) return;
const times = dataArray.map((d) => new Date(d.timestamp).getTime());
const maxTime = Math.max(...times);
const minTime = maxTime - minutes * 60 * 1000;
setFrom(new Date(minTime));
setTo(new Date(maxTime));
}
// buton function for last 30 minutes and last hour
const usageLast30Min = () =>
handleLastMinutes(
controllerCpuUsageData,
setFromDateUsage,
setToDateUsage,
30
);
const usageLastHour = () =>
handleLastMinutes(
controllerCpuUsageData,
setFromDateUsage,
setToDateUsage,
60
);
const thermalLast30Min = () =>
handleLastMinutes(
controllertemperaturecpu_thermalData,
setFromDateThermal,
setToDateThermal,
30
);
const thermalLastHour = () =>
handleLastMinutes(
controllertemperaturecpu_thermalData,
setFromDateThermal,
setToDateThermal,
60
);Change Information: If you want to change the timepsan (at the moment the 2 button have 30 minutes and 1 hour) you can simply change the 30 or the 60 into whatever you want. Those values have to be set in minutes.
All you have to do to call the function you want is to call it in the Button Component in the Linechart you want.
You can also add more if you want just like "thermalLastHour" for example.
return: zones: start end
Description: Is used to mark red Zones in the Linecharts whenever the value is above the treshold.
Code:
export function useRedZones(data: DataPoint[], threshold = 80) {
return useMemo(() => {
const zones: { start: string; end: string }[] = [];
let start: string | null = null;
for (let i = 0; i < data.length; i++) {
if (data[i].value >= threshold) {
if (start === null) start = data[i].timestamp;
} else {
if (start !== null) {
zones.push({ start, end: data[i].timestamp });
start = null;
}
}
}
if (start !== null) {
zones.push({ start, end: data[data.length - 1].timestamp });
}
return zones;
}, [data, threshold]);
}Description: Function sends GET request API call to get all available chargers with their base information: ("charger_name", "last_seen", "online", "manufacturer_name", "charger_id", "firmware_version", "state" and "created").
Code:
const resp = await axios.get<Charger[]>(
"http://127.0.0.1:8000/v1/chargers/available"
);
return resp.data;Description: Function sends GET requests API calls to get the specified telemetry data (here: CPU Usage and CPU Temperature). It combines the telemetry data with the base charger data and returns it as an object.
Code:
const [value1Res, value2Res] = await Promise.all([
axios.get<TelemetryData[]>(
`http://127.0.0.1:8000/v1/telemetry/${charger.charger_id}/controllerCpuUsage`
),
axios.get<TelemetryData[]>(
`http://127.0.0.1:8000/v1/telemetry/${charger.charger_id}/controllertemperaturecpu-thermal`
),
]);
return {
charger_id: charger.charger_id,
charger_name: charger.charger_name,
online: charger.online,
state: charger.state,
last_seen: charger.last_seen,
value1: value1Res.data[0]?.value ?? null,
value2: value2Res.data[0]?.value ?? null,
};Description: Function sends GET request API call to retrieve all favoured charger ids of the current user (based on the transferred user id).
Code:
const resp = await axios.get<string[]>(
`http://127.0.0.1:8000/v1/favorites?user_id=${userId}`
);
return resp.data;Description: Function that is called when a user clicks on the favorite/star icon. The favourites entry for the selected charger is deleted if it was already a favourite. Otherwise, a favourites entry is created.
Code:
async (chargerId: string, userId: number, isCurrentlyFavorite: boolean) => {
if (isCurrentlyFavorite) {
await axios.delete("http://127.0.0.1:8000/v1/favorites", {
data: { charger_id: chargerId, user_id: userId },
});
} else {
await axios.post("http://127.0.0.1:8000/v1/favorites", {
charger_id: chargerId,
user_id: userId,
});
}
},
[];Description: Function sends GET requests API calls to get all the anomaly data from a specific charger. It returns the "charger_id", "timestamp", "telemetry_type", "anomaly_type" and "anomaly_value".
Code:
async (chargerId: string): Promise<Anomaly[]> => {
const resp = await axios.get<Anomaly[]>(
`http://127.0.0.1:8000/v1/anomalies?charger_id=${chargerId}`
);
return resp.data;
};5.2. addAnomaly(chargerId: string, timestamp: Date, telemetry_type: string, anomaly_type: string, anomaly_value: number):
Description: Function needs to be called with a charger_id, timestamp, telemtry_type, anomaly_type and an anomaly_value to add an anomaly to the database.
Code:
async (
chargerId: string,
timestamp: Date,
telemetry_type: string,
anomaly_type: string,
anomaly_value: number
) => {
await axios.post("http://127.0.0.1:8000/v1/anomalies", {
charger_id: chargerId,
timestamp: timestamp,
telemetry_type: telemetry_type,
anomaly_type: anomaly_type,
anomaly_value: anomaly_value,
});
};Description: Function deletes a given anomaly entry based on the specified charger_id, timestamp and telemetry_type combination.
Code:
async (chargerId: string, timestamp: Date, telemetry_type: string) => {
const params = new URLSearchParams({
charger_id: chargerId,
timestamp: timestamp.toISOString(), // in ISO-Format
telemetry_type: telemetry_type,
});
await axios.delete(
`http://127.0.0.1:8000/v1/anomalies?${params.toString()}`
);
},Description:
Handles the login form submission. Sends a POST request to the backend with email and password. Based on rememberMe, it stores the token in localStorage or sessionStorage, logs the user in via context, triggers charger and telemetry data sync, and navigates to the home page after a delay.
Code:
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:8000/v1/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
const errorData = await response.json();
setMessage(errorData.detail || "Login failed");
return;
}
const data: LoginResponse = await response.json();
if (rememberMe) {
localStorage.setItem("token", data.access_token);
} else {
sessionStorage.setItem("token", data.access_token);
}
login(data.access_token);
syncChargers();
syncTelemetry();
setTimeout(() => {
navigate("/");
}, 2000);
} catch (error) {
console.error(error);
setMessage("An error occurred");
}
};Description: Handles the registration form submission. Performs validation (password match, minimum length), sends a POST request to register the user, displays success or error messages, and redirects to the login page after successful registration.
Code:
const handleRegister = async (e: React.FormEvent) => {
e.preventDefault();
setIsError(false);
if (password !== confirmPassword) {
setMessage("Passwords do not match.");
setIsError(true);
return;
}
if (password.length < 8) {
setMessage("Password should be at least 8 characters long.");
setIsError(true);
return;
}
try {
const response = await fetch("http://localhost:8000/v1/auth/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password, role: "user" }),
});
if (!response.ok) {
const errorData = await response.json();
setMessage(errorData.detail || "Registration failed");
setIsError(true);
return;
}
const data: RegistrationResponse = await response.json();
setMessage(
data.message || "Registration successful! Please check your email."
);
setIsError(false);
setTimeout(() => {
window.location.href = "/login";
}, 3000);
} catch (error) {
console.error(error);
setMessage("An error occurred.");
setIsError(true);
}
};Description:
Handles the email verification process. It extracts a token from the URL query parameters and sends a GET request to the backend to verify the userβs email address. Based on the API response, it updates the status message shown to the user.
Code:
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
const Verification: React.FC = () => {
const location = useLocation();
const [status, setStatus] = useState<string>("Verifying...");
const queryParams = new URLSearchParams(location.search);
const token = queryParams.get("token");
useEffect(() => {
if (token) {
axios
.get(`http://localhost:8000/v1/auth/verify-email?token=${token}`)
.then((_response) => {
setStatus("Email verified successfully!");
})
.catch((_error) => {
setStatus("Verification failed. Please try again.");
});
} else {
setStatus("Invalid verification link.");
}
}, [token]);
return (
<div>
<h1>{status}</h1>
</div>
);
};
export default Verification;Description:
This function is triggered when the forgot password form is submitted. It prevents the default form behavior and sends a POST request to the backend endpoint /v1/auth/forgot-password with the entered email address as JSON. If the request is successful, it displays the response message to the user. If an error occurs (e.g., network issue or backend failure), a generic error message is shown instead.
Code:
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const res = await fetch("http://localhost:8000/v1/auth/forgot-password", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
const data = await res.json();
setMessage(data.message);
} catch (error) {
setMessage("An error occurred while sending the request.");
}
};Description:
On component mount, this effect checks both localStorage and sessionStorage for a previously saved token. If found, it updates the token state and stops the loading state.
Code:
useEffect(() => {
const savedToken =
localStorage.getItem("token") || sessionStorage.getItem("token");
if (savedToken) {
setToken(savedToken);
}
setIsLoading(false);
}, []);Description:
Stores a new authentication token into sessionStorage and updates the React state to authenticate the user.
Code:
const login = (newToken: string) => {
sessionStorage.setItem("token", newToken);
setToken(newToken);
};Description:
Removes the authentication token from both sessionStorage and localStorage to log the user out of the session.
Code:
const logout = () => {
sessionStorage.removeItem("token");
localStorage.removeItem("token");
};Description: Boolean value that indicates if the user is currently authenticated, based on the presence of a valid token.
Code:
const isAuthenticated = !!token;Description:
Custom React hook that allows components to access the authentication context. Will throw an error if used outside of the AuthProvider.
Code:
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};Description: Component that provides authentication context (token, login/logout, state) to its children.
Code:
<AuthContext.Provider
value={{ isAuthenticated, token, isLoading, login, logout }}
>
{children}
</AuthContext.Provider>Description:
This component serves as a route guard. It ensures that only authenticated users can access certain routes. If authentication is still loading, it shows a loading message. If the user is not authenticated, it redirects to the login page. If authenticated, it renders the protected component (children).
Code:
import { Navigate } from "react-router-dom";
import { useAuth } from "./AuthContext";
import { JSX } from "react";
export const ProtectedRoute = ({ children }: { children: JSX.Element }) => {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) {
return (
<div className="text-center mt-10 text-gray-600">
Authentication is loading...
</div>
);
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return children;
};Description: Fetches all available telemetry types (keys) for a given charger by its ID.
Code:
const getTelemetryTypes = useCallback(
async (chargerId: string): Promise<string[]> => {
const resp = await axios.get<string[]>(
`http://127.0.0.1:8000/v1/telemetry/${chargerId}/type`
);
return resp.data;
},
[]
);Description: Retrieves telemetry data for a specific charger and telemetry key (e.g., CPU usage or temperature).
Code:
const getTelemetryData = useCallback(
async (chargerId: string, telemetryKey: string): Promise<Cpu[]> => {
const resp = await axios.get<Cpu[]>(
`http://127.0.0.1:8000/v1/telemetry/${chargerId}/${telemetryKey}?limit=1000`
);
return resp.data;
},
[]
);Description: Fetches the list of all currently available chargers.
Code:
const getAllChargers = useCallback(async (): Promise<Charger[]> => {
const resp = await axios.get<Charger[]>(
"http://127.0.0.1:8000/v1/chargers/available"
);
return resp.data;
}, []);Description: Returns a list of charger IDs that are marked as favorites for the given user.
Code:
const getFavorites = useCallback(async (userId: number): Promise<string[]> => {
const resp = await axios.get<string[]>(
`http://127.0.0.1:8000/v1/favorites?user_id=${userId}`
);
return resp.data;
}, []);12.5. toggleFavorite(chargerId: string, userId: number, isCurrentlyFavorite: boolean): Promise<void>
Description: Adds or removes a charger from the user's favorites, depending on whether it's already marked.
Code:
const toggleFavorite = useCallback(
async (chargerId: string, userId: number, isCurrentlyFavorite: boolean) => {
if (isCurrentlyFavorite) {
await axios.delete("http://127.0.0.1:8000/v1/favorites", {
data: { charger_id: chargerId, user_id: userId },
});
} else {
await axios.post("http://127.0.0.1:8000/v1/favorites", {
charger_id: chargerId,
user_id: userId,
});
}
},
[]
);Description: Combines basic charger information with selected telemetry data (CPU usage & temperature) for each charger.
Code:
const getCombinedChargerData = useCallback(
async (chargers: Charger[]): Promise<CombinedData[]> => {
const combinedData = await Promise.all(
chargers.map(async (charger) => {
try {
const [value1Res, value2Res] = await Promise.all([
axios.get<TelemetryData[]>(
`http://127.0.0.1:8000/v1/telemetry/${charger.charger_id}/controllerCpuUsage`
),
axios.get<TelemetryData[]>(
`http://127.0.0.1:8000/v1/telemetry/${charger.charger_id}/controllertemperaturecpu-thermal`
),
]);
return {
charger_id: charger.charger_id,
charger_name: charger.charger_name,
online: charger.online,
state: charger.state,
last_seen: charger.last_seen,
value1: value1Res.data[0]?.value ?? null,
value2: value2Res.data[0]?.value ?? null,
};
} catch (error) {
console.warn(
`Error at values from Charger ${charger.charger_id}`,
error
);
return {
charger_id: charger.charger_id,
charger_name: charger.charger_name,
online: charger.online,
state: charger.state,
last_seen: charger.last_seen,
value1: null,
value2: null,
};
}
})
);
return combinedData;
},
[]
);Description: Fetches telemetry anomalies associated with the specified charger.
Code:
const getAnomalies = useCallback(
async (chargerId: string): Promise<Anomaly[]> => {
const resp = await axios.get<Anomaly[]>(
`http://127.0.0.1:8000/v1/anomalies?charger_id=${chargerId}`
);
return resp.data;
},
[]
);Description: Adds an anomaly entry for a specific charger and telemetry type.
Code:
const addAnomaly = useCallback(
async (chargerId: string, timestamp: Date, telemetry_type: string) => {
await axios.post("http://127.0.0.1:8000/v1/anomalies", {
charger_id: chargerId,
timestamp: timestamp,
telemetry_type: telemetry_type,
});
},
[]
);Description: Deletes an anomaly for a specific charger based on telemetry type and timestamp.
Code:
const deleteAnomaly = useCallback(
async (chargerId: string, timestamp: Date, telemetry_type: string) => {
const params = new URLSearchParams({
charger_id: chargerId,
timestamp: timestamp.toISOString(), // in ISO-Format
telemetry_type: telemetry_type,
});
await axios.delete(
`http://127.0.0.1:8000/v1/anomalies?${params.toString()}`
);
},
[]
);Description: Triggers server-side synchronization for charger data.
Code:
const syncChargers = useCallback(async (): Promise<void> => {
try {
await axios.post("http://127.0.0.1:8000/v1/chargers/sync", null);
} catch (err) {
console.warn("syncChargers failed:", err);
}
}, []);Description: Triggers full telemetry data sync (up to 10,000 records)
Code:
const syncTelemetry = useCallback(async (): Promise<void> => {
try {
await axios.post(
"http://127.0.0.1:8000/v1/telemetry/sync?limit=10000",
null
);
} catch (err) {
console.warn("syncTelemetry failed:", err);
}
}, []);Description: Triggers a short sync for recent telemetry data (100 records).
Code:
const syncTelemetryShort = useCallback(async (): Promise<void> => {
try {
await axios.post("http://127.0.0.1:8000/v1/telemetry/sync?limit=100", null);
} catch (err) {
console.warn("syncTelemetryShort failed:", err);
}
}, []);Description: Loads CPU usage telemetry for a charger and saves it in state.
Code:
const loadCpuUsage = useCallback(
async (chargerId: string) => {
try {
// //Sync Telemetry first
// await syncTelemetryShort();
// now get the Keys
const types = await getTelemetryTypes(chargerId);
const cpuUsageKey = types.find((t) =>
t.toLowerCase().includes("controllercpuusage")
);
if (!cpuUsageKey) {
console.warn(`Usage-Key for Charger ${chargerId} not found:`, types);
setSearchError(true);
return;
}
// then get the Data
const data = await getTelemetryData(chargerId, cpuUsageKey);
// write the data in the Map
setCpuUsageMap((prev) => ({
...prev,
[chargerId]: data,
}));
setSearchError(false);
} catch (err) {
console.error("Error loading CPU Usage:", err);
setSearchError(true);
}
},
[getTelemetryTypes, getTelemetryData, syncTelemetry]
);Description: Loads CPU temperature telemetry for a charger and updates context state.
Code:
const loadCpuThermal = useCallback(
async (chargerId: string) => {
try {
// //Sync Telemetry first
// await syncTelemetryShort();
// now get the Keys
const types = await getTelemetryTypes(chargerId);
const cpuThermalKey = types.find((t) =>
t.toLowerCase().includes("controllertemperaturecpu-thermal")
);
if (!cpuThermalKey) {
console.warn(`Thermal-Key for Charger ${chargerId} not found:`, types);
setSearchError(true);
return;
}
// then get the Data
const data = await getTelemetryData(chargerId, cpuThermalKey);
// write the data in the Map
setCpuThermalMap((prev) => ({
...prev,
[chargerId]: data,
}));
setSearchError(false);
} catch (err) {
console.error("Error loading CPU Thermal:", err);
setSearchError(true);
}
},
[getTelemetryTypes, getTelemetryData, syncTelemetry]
);Description: Loads monitoring data (e.g., system and controller state) and stores it in context state.
Code:
const loadMonitoring = useCallback(async (chargerId: string) => {
try {
// get the Keys
const types = await getTelemetryTypes(chargerId);
// filter for keytypes - here all key types without CPU temp and usage
const keys = types.filter(
(t) =>
t.toLowerCase().startsWith("system") ||
t.toLowerCase().startsWith("controllerstate")
);
if (keys.length === 0) {
console.warn(
`Key with key value "system" in Charger ${chargerId} not found`,
types
);
setSearchError(true);
return;
}
// get the Data
const entries = await Promise.all(
keys.map(async (key) => {
const rawData = await getTelemetryData(chargerId, key);
const data: Monitoring[] = rawData.map((d) => ({
type: key,
value: d.value,
}));
return [key, data] as const;
})
);
// write the data in the Map
setMonitoringMap((prev) => ({
...prev,
...Object.fromEntries(entries),
}));
setSearchError(false);
} catch (err) {
console.error("Error loading CPU Usage:", err);
setSearchError(true);
}
}, []);