Bunch more new UI

This commit is contained in:
2024-03-07 02:24:23 +00:00
parent d396ec785f
commit 9fbe350a68
33 changed files with 889 additions and 161 deletions

View File

@@ -11,13 +11,18 @@
</v-list-item>
<v-list-item
prepend-icon="mdi-sun-thermometer"
title="Outside"
to="outside">
title="Outdoor"
to="outdoor">
</v-list-item>
<v-list-item
prepend-icon="mdi-home-analytics"
title="Inside"
to="inside">
title="Indoor"
to="indoor">
</v-list-item>
<v-list-item
prepend-icon="mdi-home-lightning-bolt-outline"
title="Power"
to="power">
</v-list-item>
</v-list>
</v-navigation-drawer>

View File

@@ -35,7 +35,7 @@
</DashboardItem>
</template>
<style>
<style scoped>
.power-current {
font-size: 14px;
padding: 6px 12px;

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useWeatherStore } from '@/stores/weatherStore';
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
const weatherStore = useWeatherStore();
weatherStore.start();
@@ -31,62 +32,33 @@
<tbody>
<tr>
<td className="weather-current-header">Temperature</td>
<td>
{{
weatherStore.current?.Temperature?.toFixed(2)
}}°F
</td>
<td>{{ weatherStore.current?.Temperature?.toFixed(2) }}°F</td>
</tr>
<tr v-if="weatherStore.current?.HeatIndex">
<td className="weather-current-header">Heat index</td>
<td>
{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F
</td>
<td>{{ weatherStore.current?.HeatIndex?.toFixed(2) }}°F</td>
</tr>
<tr v-if="weatherStore.current?.WindChill">
<td className="weather-current-header">Wind chill</td>
<td>
{{ weatherStore.current?.WindChill?.toFixed(2) }}°F
</td>
<td>{{ weatherStore.current?.WindChill?.toFixed(2) }}°F</td>
</tr>
<tr>
<td className="weather-current-header">Humidity</td>
<td>
{{ weatherStore.current?.Humidity?.toFixed(2) }}%
</td>
<td>{{ weatherStore.current?.Humidity?.toFixed(2) }}%</td>
</tr>
<tr>
<td className="weather-current-header">Dew point</td>
<td>
{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F
</td>
<td>{{ weatherStore.current?.DewPoint?.toFixed(2) }}°F</td>
</tr>
<tr>
<td className="weather-current-header">Pressure</td>
<td>
{{
weatherStore.current?.Pressure &&
(
weatherStore.current?.Pressure /
33.864 /
100
)?.toFixed(2)
}}"
{{ weatherStore.current?.Pressure && ConvertPascalToInchesOfMercury(weatherStore.current?.Pressure)?.toFixed(2) }}"
<span
class="pressure-trend-arrow"
:class="
rotationClass(
weatherStore.current
?.PressureDifferenceThreeHour
)
"
:title="
'3 Hour Change: ' +
weatherStore.current?.PressureDifferenceThreeHour?.toFixed(
1
)
">
:class="rotationClass(weatherStore.current?.PressureDifferenceThreeHour)"
:title="'3 Hour Change: ' + weatherStore.current?.PressureDifferenceThreeHour?.toFixed(1)">
</span>
</td>
@@ -100,11 +72,7 @@
</tr>
<tr>
<td className="weather-current-header">Rain</td>
<td>
{{
weatherStore.current?.RainLastHour?.toFixed(2)
}}" (last hour)
</td>
<td>{{ weatherStore.current?.RainLastHour?.toFixed(2) }}" (last hour)</td>
</tr>
<tr>
<td className="weather-current-header">Light</td>

View File

@@ -0,0 +1,120 @@
<script setup lang="ts">
import { createIndoorStore } from '@/stores/indoorStore';
import { ConvertCToF } from '@/temperatureConverter';
import { ConvertMillibarToInchesOfMercury } from '@/pressureConverter';
const props = defineProps({
title: { type: String, required: true },
deviceName: { type: String, required: true }
});
const indoorStore = createIndoorStore(props.deviceName);
indoorStore.start();
const airQualityDescription = (airQualityIndex: number | undefined) => {
if (!airQualityIndex) {
return '';
} else if (airQualityIndex >= 0 && airQualityIndex <= 50) {
return 'Good';
} else if (airQualityIndex >= 51 && airQualityIndex <= 100) {
return 'Moderate';
} else if (airQualityIndex >= 101 && airQualityIndex <= 150) {
return 'Unhealthy for Sensitive Groups';
} else if (airQualityIndex >= 151 && airQualityIndex <= 200) {
return 'Unhealthy';
} else if (airQualityIndex >= 201 && airQualityIndex <= 300) {
return 'Very Unhealthy';
} else if (airQualityIndex >= 301) {
return 'Hazardous';
}
return '';
};
const airQualityClass = (airQualityIndex: number | undefined) => {
if (!airQualityIndex) {
return 'aqi-none';
} else if (airQualityIndex >= 0 && airQualityIndex <= 50) {
return 'aqi-green';
} else if (airQualityIndex >= 51 && airQualityIndex <= 100) {
return 'aqi-yellow';
} else if (airQualityIndex >= 101 && airQualityIndex <= 150) {
return 'aqi-orange';
} else if (airQualityIndex >= 151 && airQualityIndex <= 200) {
return 'aqi-red';
} else if (airQualityIndex >= 201 && airQualityIndex <= 300) {
return 'aqi-purple';
} else if (airQualityIndex >= 301) {
return 'aqi-maroon';
}
return '';
};
</script>
<template>
<DashboardItem :title="title">
<div className="current">
<div v-if="!indoorStore.current">Loading...</div>
<table v-else>
<tbody>
<tr>
<td className="header">Temperature</td>
<td>{{ ConvertCToF(indoorStore.current.temperature).toFixed(2) }}°F</td>
</tr>
<tr>
<td className="header">Humidity</td>
<td>{{ indoorStore.current.humidity.toFixed(2) }}%</td>
</tr>
<tr>
<td className="header">Pressure</td>
<td>{{ ConvertMillibarToInchesOfMercury(indoorStore.current.pressure).toFixed(2) }}"</td>
</tr>
<tr>
<td className="header">Air Quality</td>
<td :class="airQualityClass(indoorStore.current.airQualityIndex)" :title="indoorStore.current.airQualityIndex.toString()">
{{ airQualityDescription(indoorStore.current.airQualityIndex) }}
</td>
</tr>
</tbody>
</table>
</div>
</DashboardItem>
</template>
<style scoped>
.current {
font-size: 14px;
padding: 6px 12px;
}
.header {
font-weight: 500;
text-align: right;
padding-right: 10px;
}
.aqi-green {
color: green;
}
.aqi-yellow {
color: goldenrod;
}
.aqi-orange {
color: orange;
}
.aqi-red {
color: red;
}
.aqi-purple {
color: purple;
}
.aqi-maroon {
color: maroon;
}
</style>

View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
import { ApexOptions } from 'apexcharts';
const props = defineProps({
type: { type: String, required: true },
title: { type: String, required: true },
unit: { type: String, required: true },
categories: { type: Array<number>, required: true },
series: { type: Array<{ name: string; data: Array<number> }>, required: true },
yAxisDecimalPoints: { type: Number, required: false, default: 0 },
valueDecimalPoints: { type: Number, required: false, default: 2 },
group: { type: String, required: false, default: undefined },
stepline: { type: Boolean, required: false, default: false },
ready: { type: Boolean, required: true }
});
var chartOptions: ApexOptions = {
chart: {
id: props.title,
animations: {
enabled: false
},
group: props.group,
toolbar: {
tools: {
pan: false
}
}
},
title: {
text: props.title,
align: 'center'
},
legend: {
itemMargin: {
vertical: 5
},
showForSingleSeries: true
},
tooltip: {
x: {
format: 'MMMM d h:mm TT'
},
y: {
formatter: (value) => {
return `${value.toFixed(props.valueDecimalPoints)}${props.unit}`;
}
}
},
xaxis: {
type: 'datetime',
categories: props.categories,
tooltip: {
enabled: false
},
labels: {
datetimeUTC: false,
datetimeFormatter: {
day: 'MMM d',
hour: 'h:mm TT'
}
}
},
yaxis: {
labels: {
formatter: (value) => {
return `${value.toFixed(props.yAxisDecimalPoints)}${props.unit}`;
}
}
},
stroke: {
width: 2,
curve: props.stepline ? 'stepline' : 'smooth'
},
dataLabels: {
enabled: false
}
};
var chartSeries: ApexAxisChartSeries = props.series;
</script>
<template>
<v-container
v-if="!props.ready"
class="fill-height loading">
<v-responsive class="align-center text-center fill-height">
<v-progress-circular
:size="50"
:width="5"
color="primary"
indeterminate></v-progress-circular>
</v-responsive>
</v-container>
<apexchart
v-else
width="100%"
height="300"
:type="props.type"
:options="chartOptions"
:series="chartSeries"></apexchart>
</template>
<style scoped>
.loading {
width: 100%;
min-height: 300px;
}
</style>

View File

@@ -3,6 +3,7 @@
import { useWeatherStore } from '@/stores/weatherStore';
import { subHours } from 'date-fns';
import { WeatherAggregates } from '@/models/weather/weather-aggregates';
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
const weatherAggregates = ref<WeatherAggregates | undefined>();
@@ -11,11 +12,9 @@
const end = new Date();
const start = subHours(end, 24);
weatherStore
.getReadingAggregate(start, end)
.then((newWeatherAggregates) => {
weatherAggregates.value = newWeatherAggregates;
});
weatherStore.getReadingAggregate(start, end).then((newWeatherAggregates) => {
weatherAggregates.value = newWeatherAggregates;
});
</script>
<template>
@@ -32,78 +31,35 @@
</tr>
<tr>
<td class="weather-summary-header">Temperature</td>
<td>
{{
weatherAggregates!.temperature.min.toFixed(2)
}}°F
</td>
<td>
{{
weatherAggregates!.temperature.average.toFixed(
2
)
}}°F
</td>
<td>
{{
weatherAggregates!.temperature.max.toFixed(2)
}}°F
</td>
<td>{{ weatherAggregates!.temperature.min.toFixed(2) }}°F</td>
<td>{{ weatherAggregates!.temperature.average.toFixed(2) }}°F</td>
<td>{{ weatherAggregates!.temperature.max.toFixed(2) }}°F</td>
</tr>
<tr>
<td class="weather-summary-header">Humidity</td>
<td>
{{ weatherAggregates!.humidity.min.toFixed(2) }}%
</td>
<td>
{{
weatherAggregates!.humidity.average.toFixed(2)
}}%
</td>
<td>
{{ weatherAggregates!.humidity.max.toFixed(2) }}%
</td>
<td>{{ weatherAggregates!.humidity.min.toFixed(2) }}%</td>
<td>{{ weatherAggregates!.humidity.average.toFixed(2) }}%</td>
<td>{{ weatherAggregates!.humidity.max.toFixed(2) }}%</td>
</tr>
<tr>
<td class="weather-summary-header">Pressure</td>
<td>
{{
(
weatherAggregates!.pressure.min /
33.864 /
100
).toFixed(2)
}}"
</td>
<td>
{{
(
weatherAggregates!.pressure.average /
33.864 /
100
).toFixed(2)
}}"
</td>
<td>
{{
(
weatherAggregates!.pressure.max /
33.864 /
100
).toFixed(2)
}}"
</td>
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.min).toFixed(2) }}"</td>
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.average).toFixed(2) }}"</td>
<td>{{ ConvertPascalToInchesOfMercury(weatherAggregates!.pressure.max).toFixed(2) }}"</td>
</tr>
<tr>
<td class="weather-summary-header">Light</td>
<td>
{{ weatherAggregates!.light.min.toFixed(2) }} lx
{{ weatherAggregates!.light.min.toFixed(2) }}
lx
</td>
<td>
{{ weatherAggregates!.light.average.toFixed(2) }} lx
{{ weatherAggregates!.light.average.toFixed(2) }}
lx
</td>
<td>
{{ weatherAggregates!.light.max.toFixed(2) }} lx
{{ weatherAggregates!.light.max.toFixed(2) }}
lx
</td>
</tr>
<tr>
@@ -113,9 +69,7 @@
mph
</td>
<td>
{{
weatherAggregates!.windSpeed.average.toFixed(2)
}}
{{ weatherAggregates!.windSpeed.average.toFixed(2) }}
mph
</td>
<td>

View File

@@ -5,10 +5,13 @@ import App from './App.vue';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import VueApexCharts from 'vue3-apexcharts';
const pinia = createPinia();
const app = createApp(App);
registerPlugins(app);
app.use(VueApexCharts);
app.use(pinia);
app.mount('#app');

View File

@@ -0,0 +1,13 @@
export interface LatestReadings {
time: string
name: string
model: string
airQualityIndex: number
colorTemperature: number
gasResistance: number
humidity: number
luminance: number
pressure: number
temperature: number
}

View File

@@ -0,0 +1,11 @@
export interface ReadingsGrouped {
bucket: string;
name: string;
averageTemperature: number;
averagePressure: number;
averageHumidity: number;
averageLuminance: number;
averageGasResistance: number;
averageColorTemperature: number;
averageAirQualityIndex: number;
}

View File

@@ -0,0 +1,5 @@
export default interface PowerHistoryGrouped {
bucket: string;
averageGeneration: number;
averageConsumption: number;
}

View File

@@ -1,4 +1,4 @@
export interface WeatherAggregates {
export default interface WeatherAggregates {
humidity: WeatherAggregate;
temperature: WeatherAggregate;
pressure: WeatherAggregate;

View File

@@ -0,0 +1,8 @@
export default interface WeatherHistoryGrouped {
bucket: string;
averageTemperature: number;
averagePressure: number;
averageLightLevel: number;
averageHumidity: number;
rainTotal: number;
}

View File

@@ -1,4 +1,4 @@
export default class WeatherValueGrouped {
bucket: string | undefined;
averageValue: number | undefined;
export default interface WeatherValueGrouped {
bucket: string;
averageValue: number;
}

View File

@@ -2,7 +2,8 @@ export enum WeatherValueType {
Humidity = 'Humidity',
HumidityTemperature = 'HumidityTemperature',
PressureTemperature = 'PressureTemperature',
Pressure = 'Pressure'
Pressure = 'Pressure',
Light = 'LightLevel'
}
export default WeatherValueType;

View File

@@ -0,0 +1,7 @@
export default interface WindHistoryGrouped {
bucket: string;
minimumSpeed: number;
averageSpeed: number;
maximumSpeed: number;
averageDirection: number;
}

View File

@@ -23,6 +23,12 @@
<v-col cols="4">
<WeatherSummary></WeatherSummary>
</v-col>
<v-col cols="2">
<Indoor title="Upstairs" deviceName="main"></Indoor>
</v-col>
<v-col cols="2">
<Indoor title="Downstairs" deviceName="basement"></Indoor>
</v-col>
</v-row>
</v-container>
</template>

View File

@@ -0,0 +1,99 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { subHours } from 'date-fns';
import { createIndoorStore } from '@/stores/indoorStore';
import { ConvertCToF } from '@/temperatureConverter';
import { ConvertMillibarToInchesOfMercury } from '@/pressureConverter';
import ValueChart from '../components/ValueChart.vue';
const indoorStore = createIndoorStore('charts');
const ready = ref(false);
const categories: number[] = [];
const mainTemperatureSeries = { name: 'Upstairs', data: [] as number[] };
const basementTemperatureSeries = { name: 'Downstairs', data: [] as number[] };
const mainHumiditySeries = { name: 'Upstairs', data: [] as number[] };
const basementHumiditySeries = { name: 'Downstairs', data: [] as number[] };
const mainPressureSeries = { name: 'Upstairs', data: [] as number[] };
const basementPressureSeries = { name: 'Downstairs', data: [] as number[] };
const end = new Date();
const start = subHours(end, 24);
indoorStore.getReadingValueHistoryGrouped(start, end, 15).then((groupedReadingsList) => {
groupedReadingsList.forEach((groupedReadings) => {
if (groupedReadings.name === 'main') {
categories.push(new Date(groupedReadings.bucket).getTime());
mainTemperatureSeries.data.push(ConvertCToF(groupedReadings.averageTemperature));
mainHumiditySeries.data.push(groupedReadings.averageHumidity);
mainPressureSeries.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
} else if (groupedReadings.name === 'basement') {
basementTemperatureSeries.data.push(ConvertCToF(groupedReadings.averageTemperature));
basementHumiditySeries.data.push(groupedReadings.averageHumidity);
basementPressureSeries.data.push(ConvertMillibarToInchesOfMercury(groupedReadings.averagePressure));
}
});
ready.value = true;
});
</script>
<template>
<v-container
fluid
class="container">
<v-row
dense
align="start">
<v-col
sm="6"
cols="12">
<ValueChart
:ready="ready"
type="line"
title="Temperature"
unit="°F"
group="indoor"
:categories="categories"
:series="[mainTemperatureSeries, basementTemperatureSeries]"></ValueChart>
</v-col>
<v-col
sm="6"
cols="12">
<ValueChart
:ready="ready"
type="line"
title="Humidity"
unit="%"
group="indoor"
:categories="categories"
:series="[mainHumiditySeries, basementHumiditySeries]"></ValueChart>
</v-col>
<v-col
sm="6"
cols="12">
<ValueChart
:ready="ready"
type="line"
title="Pressure"
unit='"'
group="indoor"
:y-axis-decimal-points="1"
:categories="categories"
:series="[mainPressureSeries, basementPressureSeries]"></ValueChart>
</v-col>
</v-row>
</v-container>
</template>
<style scoped>
.container {
height: 100%;
background-color: #fafafa;
}
</style>

View File

@@ -1,11 +0,0 @@
<template>
<v-container class="fill-height">
<v-responsive class="align-center text-center fill-height">
Inside
</v-responsive>
</v-container>
</template>
<script lang="ts" setup>
//
</script>

View File

@@ -0,0 +1,160 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { subHours } from 'date-fns';
import { useWeatherStore } from '@/stores/weatherStore';
import { ConvertPascalToInchesOfMercury } from '@/pressureConverter';
import ValueChart from '../components/ValueChart.vue';
const weatherStore = useWeatherStore();
const readingsReady = ref(false);
const windReady = ref(false);
const readingsCategories: number[] = [];
const windCategories: number[] = [];
const temperatureSeries = { name: 'Average Temperature', data: [] as number[] };
const humiditySeries = { name: 'Average Humidity', data: [] as number[] };
const pressureSeries = { name: 'Average Pressure', data: [] as number[] };
const lightSeries = { name: 'Average Light', data: [] as number[] };
const rainSeries = { name: 'Total Rain', data: [] as number[] };
const windMinimumSeries = { name: 'Minimum', data: [] as number[] };
const windAverageSeries = { name: 'Average', data: [] as number[] };
const windMaximumSeries = { name: 'Maximum', data: [] as number[] };
const end = new Date();
const start = subHours(end, 24);
weatherStore.getReadingHistoryGrouped(start, end, 15).then((groupedReadingsList) => {
groupedReadingsList.forEach((groupedReadings) => {
readingsCategories.push(new Date(groupedReadings.bucket).getTime());
temperatureSeries.data.push(groupedReadings.averageTemperature);
humiditySeries.data.push(groupedReadings.averageHumidity);
pressureSeries.data.push(ConvertPascalToInchesOfMercury(groupedReadings.averagePressure));
lightSeries.data.push(groupedReadings.averageLightLevel);
rainSeries.data.push(groupedReadings.rainTotal);
});
readingsReady.value = true;
});
weatherStore.getWindHistoryGrouped(start, end, 15).then((groupedReadingsList) => {
groupedReadingsList.forEach((groupedReadings) => {
windCategories.push(new Date(groupedReadings.bucket).getTime());
windMinimumSeries.data.push(groupedReadings.minimumSpeed);
windAverageSeries.data.push(groupedReadings.averageSpeed);
windMaximumSeries.data.push(groupedReadings.maximumSpeed);
});
windReady.value = true;
});
</script>
<template>
<v-container
fluid
class="container">
<v-row
dense
align="start">
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="readingsReady"
type="line"
title="Temperature"
unit="°F"
group="outdoor"
:categories="readingsCategories"
:series="[temperatureSeries]">
</ValueChart>
</v-col>
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="readingsReady"
type="line"
title="Humidity"
unit="%"
group="outdoor"
:categories="readingsCategories"
:series="[humiditySeries]">
</ValueChart>
</v-col>
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="readingsReady"
type="line"
title="Pressure"
unit='"'
group="outdoor"
:y-axis-decimal-points="1"
:categories="readingsCategories"
:series="[pressureSeries]">
</ValueChart>
</v-col>
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="readingsReady"
type="line"
title="Light"
unit=" lx"
group="outdoor"
:categories="readingsCategories"
:series="[lightSeries]">
</ValueChart>
</v-col>
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="readingsReady"
type="line"
title="Rain"
unit='"'
group="outdoor"
:stepline="true"
:y-axis-decimal-points="2"
:categories="readingsCategories"
:series="[rainSeries]">
</ValueChart>
</v-col>
<v-col
sm="4"
xl="6"
cols="12">
<ValueChart
:ready="windReady"
type="line"
title="Wind"
unit=" MPH"
group="outdoor"
:y-axis-decimal-points="0"
:categories="windCategories"
:series="[windMinimumSeries, windAverageSeries, windMaximumSeries]">
</ValueChart>
</v-col>
</v-row>
</v-container>
</template>
<style scoped>
.container {
height: 100%;
background-color: #fafafa;
}
</style>

View File

@@ -1,11 +0,0 @@
<template>
<v-container class="fill-height">
<v-responsive class="align-center text-center fill-height">
Outside
</v-responsive>
</v-container>
</template>
<script lang="ts" setup>
//
</script>

View File

@@ -0,0 +1,93 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { subHours } from 'date-fns';
import { usePowerStore } from '@/stores/powerStore';
import { useWeatherStore } from '@/stores/weatherStore';
import ValueChart from '../components/ValueChart.vue';
import WeatherValueType from '@/models/weather/weather-value-type';
const powerStore = usePowerStore();
const weatherStore = useWeatherStore();
const powerReady = ref(false);
const lightReady = ref(false);
const powerCategories: number[] = [];
const lightCategories: number[] = [];
const generationSeries = { name: 'Generation', data: [] as number[] };
const consumptionSeries = { name: 'Consumption', data: [] as number[] };
const lightSeries = { name: 'Average Light', data: [] as number[] };
const end = new Date();
const start = subHours(end, 24);
powerStore.getReadingHistoryGrouped(start, end, 15).then((groupedReadingsList) => {
groupedReadingsList.forEach((groupedReadings) => {
powerCategories.push(new Date(groupedReadings.bucket).getTime());
generationSeries.data.push(groupedReadings.averageGeneration);
consumptionSeries.data.push(groupedReadings.averageConsumption);
});
powerReady.value = true;
});
weatherStore.getReadingValueHistoryGrouped(WeatherValueType.Light, start, end, 15).then((groupedReadingsList) => {
groupedReadingsList.forEach((groupedReadings) => {
lightCategories.push(new Date(groupedReadings.bucket).getTime());
lightSeries.data.push(groupedReadings.averageValue);
});
lightReady.value = true;
});
</script>
<template>
<v-container
fluid
class="container">
<v-row
dense
align="start">
<v-col
sm="4"
md="6"
cols="12">
<ValueChart
:ready="powerReady"
type="line"
title="Power"
unit=" W"
group="power"
:y-axis-decimal-points="0"
:value-decimal-points="0"
:categories="powerCategories"
:series="[generationSeries, consumptionSeries]"></ValueChart>
</v-col>
<v-col
sm="4"
md="6"
cols="12">
<ValueChart
:ready="lightReady"
type="line"
title="Light"
unit=" lx"
group="power"
:categories="lightCategories"
:series="[lightSeries]">
</ValueChart>
</v-col>
</v-row>
</v-container>
</template>
<style scoped>
.container {
height: 100%;
background-color: #fafafa;
}
</style>

View File

@@ -0,0 +1,7 @@
export function ConvertMillibarToInchesOfMercury(value: number): number {
return value / 33.864;
}
export function ConvertPascalToInchesOfMercury(value: number): number {
return value / 33.864 / 100.0;
}

View File

@@ -0,0 +1,62 @@
import { defineStore } from 'pinia';
import axios from 'axios';
import Environment from '@/environment';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import { LatestReadings } from '@/models/environment.ts/latestReadings';
import { ReadingsGrouped } from '@/models/environment.ts/readingsGrouped';
export function createIndoorStore(name: string) {
return defineStore(`indoor-${name}`, {
state: () => {
return {
current: null as LatestReadings | null,
_connection: null as HubConnection | null
};
},
actions: {
async start() {
if (this._connection) {
return;
}
this._connection = new HubConnectionBuilder()
.withUrl(Environment.getUrlPrefix() + '/api/hub/environment', {
withCredentials: false
})
.build();
await this._connection.start();
this._connection.on('Latest', (message: string) => {
const latestReadings = JSON.parse(message) as LatestReadings;
if (latestReadings.name === name) {
this.$patch({ current: latestReadings });
}
});
this._connection.send('RequestLatest');
},
async stop() {
if (!this._connection) {
return;
}
await this._connection.stop();
this._connection = null;
},
async getReadingValueHistoryGrouped(start: Date, end: Date, bucketMinutes: number): Promise<ReadingsGrouped[]> {
const startString = start.toISOString();
const endString = end.toISOString();
const response = await axios.get<ReadingsGrouped[]>(
Environment.getUrlPrefix() +
`/api/environment/readings/history-grouped?start=${startString}&end=${endString}&bucketMinutes=${bucketMinutes}`
);
return response.data;
}
}
})();
}

View File

@@ -1,7 +1,9 @@
import { defineStore } from 'pinia';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import axios from 'axios';
import Environment from '@/environment';
import PowerStatus from '@/models/power/power-status';
import PowerHistoryGrouped from '@/models/power/power-history-grouped';
export const usePowerStore = defineStore('power', {
state: () => {
@@ -36,6 +38,16 @@ export const usePowerStore = defineStore('power', {
await this._connection.stop();
this._connection = null;
},
async getReadingHistoryGrouped(start: Date, end: Date, bucketMinutes: number): Promise<PowerHistoryGrouped[]> {
const startString = start.toISOString();
const endString = end.toISOString();
const response = await axios.get<PowerHistoryGrouped[]>(
Environment.getUrlPrefix() + `/api/power/status/history-grouped?start=${startString}&end=${endString}&bucketMinutes=${bucketMinutes}`
);
return response.data;
}
}
});

View File

@@ -6,7 +6,9 @@ import WeatherUpdate from '@/models/weather/weather-update';
import WeatherRecent from '@/models/weather/weather-recent';
import WeatherValueType from '@/models/weather/weather-value-type';
import WeatherValueGrouped from '@/models/weather/weather-value-grouped';
import { WeatherAggregates } from '@/models/weather/weather-aggregates';
import WeatherHistoryGrouped from '@/models/weather/weather-history-grouped';
import WindHistoryGrouped from '@/models/weather/wind-history-grouped';
import WeatherAggregates from '@/models/weather/weather-aggregates';
export const useWeatherStore = defineStore('weather', {
state: () => {
@@ -43,18 +45,11 @@ export const useWeatherStore = defineStore('weather', {
this._connection = null;
},
async getLatest(): Promise<WeatherRecent> {
const response = await axios.get<WeatherRecent>(
Environment.getUrlPrefix() + `/api/weather/readings/recent`
);
const response = await axios.get<WeatherRecent>(Environment.getUrlPrefix() + `/api/weather/readings/recent`);
return response.data;
},
async getReadingValueHistoryGrouped(
valueType: WeatherValueType,
start: Date,
end: Date,
bucketMinutes: number
): Promise<WeatherValueGrouped[]> {
async getReadingValueHistoryGrouped(valueType: WeatherValueType, start: Date, end: Date, bucketMinutes: number): Promise<WeatherValueGrouped[]> {
const startString = start.toISOString();
const endString = end.toISOString();
@@ -65,16 +60,32 @@ export const useWeatherStore = defineStore('weather', {
return response.data;
},
async getReadingAggregate(
start: Date,
end: Date
): Promise<WeatherAggregates | undefined> {
async getReadingAggregate(start: Date, end: Date): Promise<WeatherAggregates | undefined> {
const startString = start.toISOString();
const endString = end.toISOString();
const response = await axios.get<WeatherAggregates>(
Environment.getUrlPrefix() +
`/api/weather/readings/aggregate?start=${startString}&end=${endString}`
Environment.getUrlPrefix() + `/api/weather/readings/aggregate?start=${startString}&end=${endString}`
);
return response.data;
},
async getReadingHistoryGrouped(start: Date, end: Date, bucketMinutes: number): Promise<WeatherHistoryGrouped[]> {
const startString = start.toISOString();
const endString = end.toISOString();
const response = await axios.get<WeatherHistoryGrouped[]>(
Environment.getUrlPrefix() + `/api/weather/readings/history-grouped?start=${startString}&end=${endString}&bucketMinutes=${bucketMinutes}`
);
return response.data;
},
async getWindHistoryGrouped(start: Date, end: Date, bucketMinutes: number): Promise<WindHistoryGrouped[]> {
const startString = start.toISOString();
const endString = end.toISOString();
const response = await axios.get<WindHistoryGrouped[]>(
Environment.getUrlPrefix() + `/api/weather/readings/wind-history-grouped?start=${startString}&end=${endString}&bucketMinutes=${bucketMinutes}`
);
return response.data;

View File

@@ -0,0 +1,3 @@
export function ConvertCToF(value: number): number {
return (value * 9.0) / 5.0 + 32.0;
}