Skip to content

Commit

Permalink
Add recent day chart to sensor tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
bnord01 committed Jan 16, 2024
1 parent c55c847 commit c182c9d
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VITE_MOISTURE_DATA_URL=endpoint for moiture data json
VITE_BACKEND_URL=endpoint for moiture data json
2 changes: 1 addition & 1 deletion .env.production
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VITE_MOISTURE_DATA_URL=https://api.bodenfeuchte.org/mapData
VITE_BACKEND_URL=https://api.bodenfeuchte.org
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Available Scripts

Before running/building the app either create a `.env` file or set the environment variable `VITE_MOISTURE_DATA_URL`.
Before running/building the app either create a `.env` file or set the environment variable `VITE_BACKEND_URL`.

In the project directory, you can run:

Expand Down
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"date-fns": "^2.30.0",
"leaflet": "^1.9.4",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-leaflet": "^4.2.1",
"use-http": "^1.0.28"
Expand Down
100 changes: 100 additions & 0 deletions src/components/SensorChart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
Tooltip,
Legend,
ChartData,
} from "chart.js";
import { Chart } from "react-chartjs-2";
import { SensorDetails, SensorInfo } from "../../model/models";
import useSensorDetails from "../../hooks/useSensorDetails";

// ChartJS setup
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, BarElement, Tooltip, Legend);

const recentDaysChartOptions = {
responsive: true,
interaction: {
mode: "index" as const,
intersect: false,
},
plugins: {
legend: {
position: "top" as const,
labels: {
boxWidth: 20,
boxHeight: 1,
},
},
},
maintainAspectRatio: false,
scales: {
moisture: {
type: "linear" as const,
display: true,
position: "left" as const,
ticks: {
callback: (value: any, index: any, ticks: any) => value + " %",
},
suggestMin: 0,
suggestMax: 50,
},
// precipitation: {
// type: 'linear' as const,
// display: true,
// position: 'right' as const,
// suggestedMax: 10,
// grid: {
// drawOnChartArea: false,
// },
// ticks: {
// callback: (value: any, index: any, ticks: any) => (value + " mm")
// }
// },
},
};

function extractRecentDaysDataset(details: SensorDetails): ChartData<"bar" | "line", number[], string> {
const colors = {
red: "rgb(255, 99, 132)",
orange: "rgb(255, 159, 64)",
yellow: "rgb(255, 205, 86)",
green: "rgb(75, 192, 192)",
blue: "rgb(54, 162, 235)",
purple: "rgb(153, 102, 255)",
grey: "rgb(201, 203, 207)",
};
return {
labels: details.measurements.map((d) => d.timestamp.toLocaleDateString()),
datasets: [
{
label: "Tageswert",
type: "line",
data: details.measurements.map((d) => d.moisture),
borderColor: colors.green,
backgroundColor: colors.green,
yAxisID: "moisture",
},
{
label: "Globales Mittel",
type: "line",
data: details.peerMeasurements.map((d) => d.moisture),
borderColor: colors.grey,
backgroundColor: colors.grey,
yAxisID: "moisture",
},
],
};
}

export default function SensorChart({ sensorInfo }: { sensorInfo: SensorInfo }) {
const { details, loading, error } = useSensorDetails(sensorInfo);
if (details) return <Chart type="line" options={recentDaysChartOptions} data={extractRecentDaysDataset(details)} />;
else if (loading) return "Loading";
else if (error) return "Error";
return "No chart";
}
9 changes: 8 additions & 1 deletion src/components/SensorTooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FontWeights, getTheme, mergeStyleSets, Text } from "@fluentui/react";
import { SensorInfo } from "../../model/models";
import SensorChart from "../SensorChart";

// Styles
const theme = getTheme();
Expand All @@ -18,6 +19,9 @@ const styles = mergeStyleSets({
height: "100%",
padding: "0 18px 15px",
},
chart: {
minHeight: "200px",
},
subtext: [
theme.fonts.small,
{
Expand Down Expand Up @@ -48,7 +52,7 @@ const SensorTooltip = ({ record }: { record: SensorInfo }) => {
<thead>
<tr>
<td></td>
<td>tagesmittel</td>
<td>Tagesmittel</td>
<td>letzte</td>
</tr>
</thead>
Expand Down Expand Up @@ -86,6 +90,9 @@ const SensorTooltip = ({ record }: { record: SensorInfo }) => {
Letzte Aktualisierung: {record.last_updated.toLocaleString()}
</Text>
</div>
<div className={styles.chart}>
<SensorChart sensorInfo={record} />
</div>
</div>
</>
);
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useMoistureData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { MoistureData, MoistureDataDto } from "../model/models";
import add from "date-fns/add";
import { useCallback, useEffect, useRef, useState } from "react";

const MOISTURE_DATA_URL = import.meta.env.VITE_MOISTURE_DATA_URL;
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;
const MOISTURE_DATA_URL = `${BACKEND_URL}/mapData`;

const MOCK_DATA: MoistureDataDto = {
timestamp: "1970-01-01 00:00:00",
Expand Down Expand Up @@ -83,7 +84,7 @@ export default function useMoistureData(): MoistureState {
};
}

const data = dto ? processData(dto) : undefined;
const data = !error && dto ? processData(dto) : undefined;
validTo.current = data ? add(data.timestamp, { days: 1, minutes: 1 }) : add(new Date(), { minutes: 5 });

return { loading, error, data };
Expand Down
50 changes: 50 additions & 0 deletions src/hooks/useSensorDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import useFetch from "use-http";
import { MoistureMeasurement, SensorDetails, SensorInfo } from "../model/models";

const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;

interface SensorDetailsDto {
sensor: Array<{
moisture: number;
time: string;
}>;
peers: Array<{
moisture: number;
time: string;
}>;
}

function processData(sensorInfo: SensorInfo, data: SensorDetailsDto): SensorDetails {
function toDate(timestamp: string): Date {
return new Date(/\+|Z/i.test(timestamp) ? timestamp : timestamp + "Z");
}
function processMeasurement({ moisture, time }: { moisture: number; time: string }): MoistureMeasurement {
return { moisture, timestamp: toDate(time) };
}

const measurements = data.sensor.map(processMeasurement);
const peerMeasurements = data.peers.map(processMeasurement);

return {
measurements,
peerMeasurements,
info: sensorInfo,
};
}

interface SensorDetailsState {
loading: Boolean;
error: Error | undefined;
details: SensorDetails | undefined;
}

export default function useSensorDetails(sensorInfo: SensorInfo): SensorDetailsState {
const url = `${BACKEND_URL}/sensorData/${sensorInfo.device}?records=7&resolution=1d`;

const fetchResult = useFetch<SensorDetailsDto>(url, [sensorInfo]);
const { loading, error, data: dto } = fetchResult;

const details = !error && dto ? processData(sensorInfo, dto) : undefined;

return { loading, error, details };
}
11 changes: 11 additions & 0 deletions src/model/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,14 @@ export interface MoistureData {
records: SensorInfo[];
timestamp: Date;
}

export interface MoistureMeasurement {
timestamp: Date;
moisture: number;
}

export interface SensorDetails {
info: SensorInfo;
measurements: MoistureMeasurement[];
peerMeasurements: MoistureMeasurement[];
}
2 changes: 1 addition & 1 deletion types/vite.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
interface ImportMetaEnv {
readonly DEV: boolean;
readonly VITE_MOISTURE_DATA_URL: string;
readonly VITE_BACKEND_URL: string;
}

interface ImportMeta {
Expand Down

0 comments on commit c182c9d

Please sign in to comment.