mirror of
				https://github.com/KevinMidboe/brewPi.git
				synced 2025-10-29 16:50:12 +00:00 
			
		
		
		
	/graph API endpoint for proxy to elastic, all use this endpoint or lib/server functions to get data
This commit is contained in:
		| @@ -83,8 +83,8 @@ function buildQuery(field: String, from: Date, to: Date, interval: String) { | |||||||
|           { |           { | ||||||
|             range: { |             range: { | ||||||
|               '@timestamp': { |               '@timestamp': { | ||||||
|                 gte: toDateString, |                 gte: fromDateString, | ||||||
|                 lte: fromDateString, |                 lte: toDateString, | ||||||
|                 format: 'strict_date_optional_time' |                 format: 'strict_date_optional_time' | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
| @@ -138,7 +138,7 @@ function calculateInterval(from, to, interval, size) { | |||||||
|   if (interval !== 'auto') { |   if (interval !== 'auto') { | ||||||
|     return interval; |     return interval; | ||||||
|   } |   } | ||||||
|   const dateMathInterval = roundInterval((from - to) / size); |   const dateMathInterval = roundInterval((to - from) / size); | ||||||
|   // const dateMathIntervalMs = toMS(dateMathInterval);
 |   // const dateMathIntervalMs = toMS(dateMathInterval);
 | ||||||
|   // const minMs = toMS(min);
 |   // const minMs = toMS(min);
 | ||||||
|   // if (dateMathIntervalMs !== undefined && minMs !== undefined && dateMathIntervalMs < minMs) {
 |   // if (dateMathIntervalMs !== undefined && minMs !== undefined && dateMathIntervalMs < minMs) {
 | ||||||
| @@ -148,6 +148,7 @@ function calculateInterval(from, to, interval, size) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function parseTempResponse(data: IESTelemetry): IChartFrame[] { | function parseTempResponse(data: IESTelemetry): IChartFrame[] { | ||||||
|  |   console.log('got temp response:', data); | ||||||
|   return data?.aggregations?.data?.buckets.map((bucket) => { |   return data?.aggregations?.data?.buckets.map((bucket) => { | ||||||
|     return { |     return { | ||||||
|       value: bucket?.maxValue?.value, |       value: bucket?.maxValue?.value, | ||||||
| @@ -161,12 +162,7 @@ function parseLatestResponse(data: IESTelemetry) { | |||||||
|   return data?.hits?.hits[0]?._source; |   return data?.hits?.hits[0]?._source; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function fetchTemperature( | export function fetchTemperature(from: Date, to: Date, size: number = 50): Promise<IChartFrame[]> { | ||||||
|   from: Date, |  | ||||||
|   to: Date, |  | ||||||
|   size: number = 50, |  | ||||||
|   fetch: Function |  | ||||||
| ): Promise<IChartFrame[]> { |  | ||||||
|   const fromMS = from.getTime(); |   const fromMS = from.getTime(); | ||||||
|   const toMS = to.getTime(); |   const toMS = to.getTime(); | ||||||
|   const interval = calculateInterval(fromMS, toMS, 'auto', size); |   const interval = calculateInterval(fromMS, toMS, 'auto', size); | ||||||
| @@ -181,18 +177,14 @@ export function fetchTemperature( | |||||||
|     }, |     }, | ||||||
|     body: JSON.stringify(esSearchQuery) |     body: JSON.stringify(esSearchQuery) | ||||||
|   }; |   }; | ||||||
|  |   console.log('temp options:', options); | ||||||
| 
 | 
 | ||||||
|   return fetch(TELEMETRY_ENDPOINT, options) |   return fetch(TELEMETRY_ENDPOINT, options) | ||||||
|     .then((resp) => resp.json()) |     .then((resp) => resp.json()) | ||||||
|     .then(parseTempResponse); |     .then(parseTempResponse); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function fetchHumidity( | export function fetchHumidity(from: Date, to: Date, size: number = 50): Promise<IChartFrame[]> { | ||||||
|   from: Date, |  | ||||||
|   to: Date, |  | ||||||
|   size: number = 50, |  | ||||||
|   fetch: Function |  | ||||||
| ): Promise<IChartFrame[]> { |  | ||||||
|   const fromMS = from.getTime(); |   const fromMS = from.getTime(); | ||||||
|   const toMS = to.getTime(); |   const toMS = to.getTime(); | ||||||
|   const interval = calculateInterval(fromMS, toMS, 'auto', size); |   const interval = calculateInterval(fromMS, toMS, 'auto', size); | ||||||
| @@ -213,12 +205,7 @@ export function fetchHumidity( | |||||||
|     .then(parseTempResponse); |     .then(parseTempResponse); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function fetchPressure( | export function fetchPressure(from: Date, to: Date, size: number = 50): Promise<IChartFrame[]> { | ||||||
|   from: Date, |  | ||||||
|   to: Date, |  | ||||||
|   size: number = 50, |  | ||||||
|   fetch: Function |  | ||||||
| ): Promise<IChartFrame[]> { |  | ||||||
|   const fromMS = from.getTime(); |   const fromMS = from.getTime(); | ||||||
|   const toMS = to.getTime(); |   const toMS = to.getTime(); | ||||||
|   const interval = calculateInterval(fromMS, toMS, 'auto', size); |   const interval = calculateInterval(fromMS, toMS, 'auto', size); | ||||||
| @@ -1,7 +1,5 @@ | |||||||
| import { getLatestInsideReadings, getLatestOutsideReadings } from '$lib/graphQueryGenerator'; |  | ||||||
| import type { PageServerLoad } from './$types'; | import type { PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
| let DEFAULT_MINUTES = 14400; |  | ||||||
| const host = 'http://brewpi.schleppe:5000'; | const host = 'http://brewpi.schleppe:5000'; | ||||||
| const sensorsUrl = `${host}/api/sensors`; | const sensorsUrl = `${host}/api/sensors`; | ||||||
| const relaysUrl = `${host}/api/relays`; | const relaysUrl = `${host}/api/relays`; | ||||||
| @@ -24,8 +22,6 @@ async function getRelays() { | |||||||
|  |  | ||||||
| export const load: PageServerLoad = async () => { | export const load: PageServerLoad = async () => { | ||||||
|   const [sensors, relays] = await Promise.all([getSensors(), getRelays()]); |   const [sensors, relays] = await Promise.all([getSensors(), getRelays()]); | ||||||
|   console.log('got sensors and relays'); |  | ||||||
|   console.log(sensors, relays); |  | ||||||
|  |  | ||||||
|   const inside = sensors.find((sensor) => sensor.location === 'inside'); |   const inside = sensors.find((sensor) => sensor.location === 'inside'); | ||||||
|   const outside = sensors.find((sensor) => sensor.location === 'outside'); |   const outside = sensors.find((sensor) => sensor.location === 'outside'); | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								src/routes/api/graph/[unit]/+server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/routes/api/graph/[unit]/+server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | import { json, RequestEvent } from '@sveltejs/kit'; | ||||||
|  | import { | ||||||
|  |   fetchTemperature, | ||||||
|  |   fetchHumidity, | ||||||
|  |   fetchPressure | ||||||
|  | } from '../../../../lib/server/graphQueryGenerator'; | ||||||
|  | import type { RequestHandler } from './$types'; | ||||||
|  |  | ||||||
|  | const UNITS = ['temperature', 'humidity', 'pressure']; | ||||||
|  | const UNITS_STRING = UNITS.join(', '); | ||||||
|  |  | ||||||
|  | export const POST = (async (event: RequestEvent) => { | ||||||
|  |   const { request, params } = event; | ||||||
|  |  | ||||||
|  |   const { unit } = params; | ||||||
|  |   if (!unit || UNITS.indexOf(unit) == -1) { | ||||||
|  |     return json({ | ||||||
|  |       success: false, | ||||||
|  |       message: `Unit ${unit} not found. Choose from: ${UNITS_STRING}` | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const bodyData = await request.json(); | ||||||
|  |   let data; | ||||||
|  |   let { from, to } = bodyData; | ||||||
|  |   const { size } = bodyData; | ||||||
|  |   from = new Date(from); | ||||||
|  |   to = new Date(to); | ||||||
|  |  | ||||||
|  |   if (unit === 'temperature') { | ||||||
|  |     data = await fetchTemperature(from, to, size); | ||||||
|  |   } else if (unit === 'humidity') { | ||||||
|  |     data = await fetchHumidity(from, to, size); | ||||||
|  |   } else if (unit === 'pressure') { | ||||||
|  |     data = await fetchPressure(from, to, size); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return json({ success: true, data }); | ||||||
|  | }) satisfies RequestHandler; | ||||||
| @@ -1,13 +1,21 @@ | |||||||
| import { fetchTemperature, fetchHumidity, fetchPressure } from '$lib/graphQueryGenerator'; | import { | ||||||
|  |   fetchTemperature, | ||||||
|  |   fetchHumidity, | ||||||
|  |   fetchPressure | ||||||
|  | } from '../../lib/server/graphQueryGenerator'; | ||||||
| import type { PageServerLoad } from './$types'; | import type { PageServerLoad } from './$types'; | ||||||
| import type IChartFrame from '$lib/interfaces/IChartFrame'; | import type IChartFrame from '../../lib/interfaces/IChartFrame'; | ||||||
|  |  | ||||||
| let DEFAULT_MINUTES = 10080; | const DEFAULT_MINUTES = 10080; | ||||||
|  |  | ||||||
| export const load: PageServerLoad = async ({ fetch }) => { | export const load: PageServerLoad = async ({ fetch }) => { | ||||||
|   const temperatureData: IChartFrame[] = await getTemp(DEFAULT_MINUTES, fetch); |   const to = new Date(); | ||||||
|   const humidityData: IChartFrame[] = await getHumidity(DEFAULT_MINUTES, fetch); |   const from = new Date(to.getTime() - DEFAULT_MINUTES * 60 * 1000); | ||||||
|   const pressureData: IChartFrame[] = await getPressure(DEFAULT_MINUTES, fetch); |   const size = 40; | ||||||
|  |  | ||||||
|  |   const temperatureData: IChartFrame[] = await fetchTemperature(from, to, size); | ||||||
|  |   const humidityData: IChartFrame[] = await fetchHumidity(from, to, size); | ||||||
|  |   const pressureData: IChartFrame[] = await fetchPressure(from, to, size); | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     temperatureData, |     temperatureData, | ||||||
| @@ -16,23 +24,3 @@ export const load: PageServerLoad = async ({ fetch }) => { | |||||||
|     DEFAULT_MINUTES |     DEFAULT_MINUTES | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| function getSensor(func: Function, minutes: number, fetch: Function) { |  | ||||||
|   const from: Date = new Date(); |  | ||||||
|   const to = new Date(from.getTime() - minutes * 60 * 1000); |  | ||||||
|   const size = 40; |  | ||||||
|  |  | ||||||
|   return func(from, to, size, fetch); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getTemp(minutes: number, fetch: Function): IChartFrame[] { |  | ||||||
|   return getSensor(fetchTemperature, minutes, fetch); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getHumidity(minutes: number, fetch: Function): IChartFrame[] { |  | ||||||
|   return getSensor(fetchHumidity, minutes, fetch); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getPressure(minutes: number, fetch: Function): IChartFrame[] { |  | ||||||
|   return getSensor(fetchPressure, minutes, fetch); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,55 +1,48 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import { onMount } from 'svelte'; |   import { onMount } from 'svelte'; | ||||||
|   import { fetchTemperature, fetchHumidity, fetchPressure } from '$lib/graphQueryGenerator'; |   import Graph from '../../lib/components/Graph.svelte'; | ||||||
|   import Graph from '$lib/components/Graph.svelte'; |   import type IChartFrame from '../../lib/interfaces/IChartFrame'; | ||||||
|   import type IChartFrame from '$lib/interfaces/IChartFrame'; |  | ||||||
|   import type { PageData } from './$types'; |   import type { PageData } from './$types'; | ||||||
|  |  | ||||||
|   export let data: PageData |   export let data: PageData; | ||||||
|  |  | ||||||
|   let temperatureData: IChartFrame[] = data?.temperatureData; |   let temperatureData: IChartFrame[] = data?.temperatureData; | ||||||
|   let humidityData: IChartFrame[] = data?.temperatureData; |   let humidityData: IChartFrame[] = data?.humidityData; | ||||||
|   let pressureData: IChartFrame[] = data?.temperatureData; |   let pressureData: IChartFrame[] = data?.pressureData; | ||||||
|   let DEFAULT_MINUTES: number = data?.DEFAULT_MINUTES |   let DEFAULT_MINUTES: number = data?.DEFAULT_MINUTES; | ||||||
|   let minutes: number = DEFAULT_MINUTES; |   let minutes: number = DEFAULT_MINUTES; | ||||||
|  |  | ||||||
|   const buttonMinutes = [{ |   async function fetchData(unit: string, from: Date, to: Date, size: number) { | ||||||
|       value: 15, |     const options = { | ||||||
|       name: 'Last 15 minutes' |       method: 'POST', | ||||||
|     }, { |       body: JSON.stringify({ from, to, size }) | ||||||
|       value: 60, |     }; | ||||||
|       name: 'Last hour' |  | ||||||
|     }, { |     return fetch(`/api/graph/${unit}`, options).then((resp) => resp.json()); | ||||||
|       value: 1440, |   } | ||||||
|       name: 'Last day' |  | ||||||
|     }, { |   const buttonMinutes = [ | ||||||
|       value: 10080, |     { value: 15, name: 'Last 15 minutes' }, | ||||||
|       name: 'Last week' |     { value: 60, name: 'Last hour' }, | ||||||
|     }, { |     { value: 360, name: 'Last 6 hours' }, | ||||||
|       value: 43200, |     { value: 1440, name: 'Last day' }, | ||||||
|       name: 'Last month' |     { value: 10080, name: 'Last week' }, | ||||||
|     }, { |     { value: 43200, name: 'Last month' }, | ||||||
|       value: 129600, |     { value: 129600, name: 'Last 3 months' }, | ||||||
|       name: 'Last 3 months' |     { value: 259200, name: 'Last 6 months' }, | ||||||
|     }, { |     { value: 518400, name: 'Last year' } | ||||||
|       value: 259200, |   ]; | ||||||
|       name: 'Last 6 months' |  | ||||||
|     }, { |  | ||||||
|       value: 518400, |  | ||||||
|       name: 'Last year', |  | ||||||
|     }] |  | ||||||
|  |  | ||||||
|   function reload(mins: number) { |   function reload(mins: number) { | ||||||
|     minutes = mins |     minutes = mins; | ||||||
|     const from: Date = new Date(); |     const to: Date = new Date(); | ||||||
|     const to = new Date(from.getTime() - minutes * 60 * 1000); |     const from = new Date(to.getTime() - minutes * 60 * 1000); | ||||||
|     const size = 40; |     const size = 40; | ||||||
|  |  | ||||||
|     fetchTemperature(from, to, size, window.fetch).then((resp) => (temperatureData = resp)); |     fetchData('temperature', from, to, size).then((resp) => (temperatureData = resp?.data)); | ||||||
|     fetchHumidity(from, to, size, window.fetch).then((resp) => (humidityData = resp)); |     fetchData('humidity', from, to, size).then((resp) => (humidityData = resp?.data)); | ||||||
|     fetchPressure(from, to, size, window.fetch).then((resp) => (pressureData = resp)); |     fetchData('pressure', from, to, size).then((resp) => (pressureData = resp?.data)); | ||||||
|   } |   } | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <!-- <h1>Server: {emoji.emoji}</h1> --> | <!-- <h1>Server: {emoji.emoji}</h1> --> | ||||||
|  |  | ||||||
| @@ -57,24 +50,27 @@ | |||||||
|  |  | ||||||
| <div class="button-wrapper"> | <div class="button-wrapper"> | ||||||
|   {#each buttonMinutes as button} |   {#each buttonMinutes as button} | ||||||
|     <button on:click={() => reload(button.value)} class="{button.value === minutes ? 'selected' : ''}">{ button.name }</button> |     <button | ||||||
|  |       on:click="{() => reload(button.value)}" | ||||||
|  |       class="{button.value === minutes ? 'selected' : ''}">{button.name}</button | ||||||
|  |     > | ||||||
|   {/each} |   {/each} | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <section class="graphs"> | <section class="graphs"> | ||||||
|   {#if temperatureData} |   {#if temperatureData} | ||||||
|     <div class="graphWrapper"> |     <div class="card"> | ||||||
|       <Graph dataFrames={temperatureData} name="Temperature" /> |       <Graph dataFrames="{temperatureData}" name="Temperature" /> | ||||||
|     </div> |     </div> | ||||||
|   {/if} |   {/if} | ||||||
|   {#if humidityData} |   {#if humidityData} | ||||||
|     <div class="graphWrapper"> |     <div class="card"> | ||||||
|       <Graph dataFrames={humidityData} name="Humidity" beginAtZero={false} /> |       <Graph dataFrames="{humidityData}" name="Humidity" /> | ||||||
|     </div> |     </div> | ||||||
|   {/if} |   {/if} | ||||||
|   {#if pressureData} |   {#if pressureData} | ||||||
|     <div class="graphWrapper"> |     <div class="card"> | ||||||
|       <Graph dataFrames={pressureData} name="Pressure" beginAtZero={false} /> |       <Graph dataFrames="{pressureData}" name="Pressure" /> | ||||||
|     </div> |     </div> | ||||||
|   {/if} |   {/if} | ||||||
| </section> | </section> | ||||||
| @@ -89,17 +85,21 @@ | |||||||
|     width: 100%; |     width: 100%; | ||||||
|  |  | ||||||
|     @include mobile { |     @include mobile { | ||||||
|       grid-template-columns: 1fr; |       display: block; | ||||||
|  |       > *:not(*:first-child) { | ||||||
|  |         margin-top: 1rem; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     .graphWrapper { |     .card { | ||||||
|       max-width: 100vw; |       padding: 1rem; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .button-wrapper { |   .button-wrapper { | ||||||
|     display: flex; |     display: flex; | ||||||
|     width: min-content; |     margin: 1rem 0; | ||||||
|  |     overflow-y: scroll; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   button { |   button { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user