diff --git a/WebDisplay/components.d.ts b/WebDisplay/components.d.ts index a4684a4..7f11273 100644 --- a/WebDisplay/components.d.ts +++ b/WebDisplay/components.d.ts @@ -16,6 +16,7 @@ declare module 'vue' { Indoor: typeof import('./src/components/Indoor.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + TimeRange: typeof import('./src/components/TimeRange.vue')['default'] ValueChart: typeof import('./src/components/ValueChart.vue')['default'] WeatherSummary: typeof import('./src/components/WeatherSummary.vue')['default'] } diff --git a/WebDisplay/package.json b/WebDisplay/package.json index 6c88f6b..687f5e2 100644 --- a/WebDisplay/package.json +++ b/WebDisplay/package.json @@ -11,6 +11,7 @@ "@mdi/font": "7.0.96", "@microsoft/signalr": "^8.0.0", "@types/suncalc": "^1.9.2", + "@vuepic/vue-datepicker": "^8.2.0", "apexcharts": "^3.46.0", "axios": "^1.6.7", "core-js": "^3.34.0", diff --git a/WebDisplay/pnpm-lock.yaml b/WebDisplay/pnpm-lock.yaml index 603ad9d..f2182c3 100644 --- a/WebDisplay/pnpm-lock.yaml +++ b/WebDisplay/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@types/suncalc': specifier: ^1.9.2 version: 1.9.2 + '@vuepic/vue-datepicker': + specifier: ^8.2.0 + version: 8.2.0(vue@3.4.21) apexcharts: specifier: ^3.46.0 version: 3.46.0 @@ -850,6 +853,16 @@ packages: /@vue/shared@3.4.21: resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + /@vuepic/vue-datepicker@8.2.0(vue@3.4.21): + resolution: {integrity: sha512-H3X9/XRvF4/ttdgxgNhFCp9m9gt9UVsiuv8FXOfAPpfQvVlmlTHZ3yf3E5oAxzP/INVhBIJCGrkijhCl5+UAPg==} + engines: {node: '>=18.12.0'} + peerDependencies: + vue: '>=3.2.0' + dependencies: + date-fns: 3.3.1 + vue: 3.4.21(typescript@5.3.3) + dev: false + /@vuetify/loader-shared@2.0.2(vue@3.4.21)(vuetify@3.5.6): resolution: {integrity: sha512-91itRxkTRKUhEZhWR/ICfYml1WkVxC7/DIeJEyUzzzLQxRo9wpyk+rhFtwHUUJE2O8wL1VG9L6TkR5SIUswwKQ==} peerDependencies: diff --git a/WebDisplay/src/components/TimeRange.vue b/WebDisplay/src/components/TimeRange.vue new file mode 100644 index 0000000..f25ca62 --- /dev/null +++ b/WebDisplay/src/components/TimeRange.vue @@ -0,0 +1,188 @@ + + + + + + + mdi-refresh + + + + + {{ timeSpanItems[timeSpan] }} + + + + + {{ item }} + + + + + mdi-arrow-left + + + + mdi-arrow-right + + + Today + + + + + + diff --git a/WebDisplay/src/components/ValueChart.vue b/WebDisplay/src/components/ValueChart.vue index 7644afe..6cd8f9d 100644 --- a/WebDisplay/src/components/ValueChart.vue +++ b/WebDisplay/src/components/ValueChart.vue @@ -11,7 +11,12 @@ 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 } + ready: { type: Boolean, required: true }, + yAxisMinimum: { type: Number, required: false, default: undefined }, + yAxisMaximum: { type: Number, required: false, default: undefined }, + tickAmount: { type: Number, required: false, default: undefined }, + lineSize: { type: Number, required: false, default: 2 }, + markerSize: { type: Number, required: false, default: 0 } }); var chartOptions: ApexOptions = { @@ -66,12 +71,18 @@ formatter: (value) => { return `${value.toFixed(props.yAxisDecimalPoints)}${props.unit}`; } - } + }, + tickAmount: props.tickAmount, + min: props.yAxisMinimum, + max: props.yAxisMaximum }, stroke: { - width: 2, + width: props.lineSize, curve: props.stepline ? 'stepline' : 'smooth' }, + markers: { + size: props.markerSize + }, dataLabels: { enabled: false } @@ -96,7 +107,7 @@ @@ -106,7 +117,7 @@ diff --git a/WebDisplay/src/pages/outdoor.vue b/WebDisplay/src/pages/outdoor.vue index dfbcaa2..fd4ad7e 100644 --- a/WebDisplay/src/pages/outdoor.vue +++ b/WebDisplay/src/pages/outdoor.vue @@ -4,6 +4,9 @@ import { useWeatherStore } from '@/stores/weatherStore'; import { ConvertPascalToInchesOfMercury } from '@/pressureConverter'; import ValueChart from '../components/ValueChart.vue'; + import WindDirectionNumber from '@/models/weather/wind-direction-number'; + import TimeRange from '@/components/TimeRange.vue'; + import TimeSpan from '@/models/time-span'; const weatherStore = useWeatherStore(); @@ -22,133 +25,192 @@ const windMinimumSeries = { name: 'Minimum', data: [] as number[] }; const windAverageSeries = { name: 'Average', data: [] as number[] }; const windMaximumSeries = { name: 'Maximum', data: [] as number[] }; + const windDirectionSeries = { name: 'Average Direction', data: [] as number[] }; - const end = new Date(); - const start = subHours(end, 24); + const end = ref(new Date()); + const start = ref(subHours(end.value, 24)); + const timeSpan = ref(TimeSpan.Last24Hours); - weatherStore.getReadingHistoryGrouped(start, end, 15).then((groupedReadingsList) => { - groupedReadingsList.forEach((groupedReadings) => { - readingsCategories.push(new Date(groupedReadings.bucket).getTime()); + const load = () => { + readingsReady.value = false; + readingsCategories.length = 0; + temperatureSeries.data.length = 0; + humiditySeries.data.length = 0; + pressureSeries.data.length = 0; + lightSeries.data.length = 0; + rainSeries.data.length = 0; - 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); + windReady.value = false; + windCategories.length = 0; + windMinimumSeries.data.length = 0; + windAverageSeries.data.length = 0; + windMaximumSeries.data.length = 0; + windDirectionSeries.data.length = 0; + + weatherStore.getReadingHistoryGrouped(start.value, end.value, 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; }); - readingsReady.value = true; - }); + weatherStore.getWindHistoryGrouped(start.value, end.value, 15).then((groupedReadingsList) => { + groupedReadingsList.forEach((groupedReadings) => { + windCategories.push(new Date(groupedReadings.bucket).getTime()); - 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); - windMinimumSeries.data.push(groupedReadings.minimumSpeed); - windAverageSeries.data.push(groupedReadings.averageSpeed); - windMaximumSeries.data.push(groupedReadings.maximumSpeed); + windDirectionSeries.data.push(groupedReadings.averageDirection); + }); + + windReady.value = true; }); + }; - windReady.value = true; - }); + load(); - - - - - - - - - - - - - - - - - - - - - - - - - - + { + timeSpan = value.timeSpan; + start = value.start; + end = value.end; + + load(); + } + " + @refresh="load"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -156,5 +218,10 @@ .container { height: 100%; background-color: #fafafa; + padding: 0; + } + + .content { + padding: 10px; } diff --git a/WebDisplay/src/pages/power.vue b/WebDisplay/src/pages/power.vue index 9d80f35..614d750 100644 --- a/WebDisplay/src/pages/power.vue +++ b/WebDisplay/src/pages/power.vue @@ -4,7 +4,9 @@ import { usePowerStore } from '@/stores/powerStore'; import { useWeatherStore } from '@/stores/weatherStore'; import ValueChart from '../components/ValueChart.vue'; + import TimeRange from '@/components/TimeRange.vue'; import WeatherValueType from '@/models/weather/weather-value-type'; + import TimeSpan from '@/models/time-span'; const powerStore = usePowerStore(); const weatherStore = useWeatherStore(); @@ -20,66 +22,96 @@ const lightSeries = { name: 'Average Light', data: [] as number[] }; - const end = new Date(); - const start = subHours(end, 24); + const end = ref(new Date()); + const start = ref(subHours(end.value, 24)); + const timeSpan = ref(TimeSpan.Last24Hours); - powerStore.getReadingHistoryGrouped(start, end, 15).then((groupedReadingsList) => { - groupedReadingsList.forEach((groupedReadings) => { - powerCategories.push(new Date(groupedReadings.bucket).getTime()); + const load = () => { + powerReady.value = false; + powerCategories.length = 0; + generationSeries.data.length = 0; + consumptionSeries.data.length = 0; - generationSeries.data.push(groupedReadings.averageGeneration); - consumptionSeries.data.push(groupedReadings.averageConsumption); + lightReady.value = false; + lightCategories.length = 0; + lightSeries.data.length = 0; + + powerStore.getReadingHistoryGrouped(start.value, end.value, 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; }); - powerReady.value = true; - }); + weatherStore.getReadingValueHistoryGrouped(WeatherValueType.Light, start.value, end.value, 15).then((groupedReadingsList) => { + groupedReadingsList.forEach((groupedReadings) => { + lightCategories.push(new Date(groupedReadings.bucket).getTime()); - weatherStore.getReadingValueHistoryGrouped(WeatherValueType.Light, start, end, 15).then((groupedReadingsList) => { - groupedReadingsList.forEach((groupedReadings) => { - lightCategories.push(new Date(groupedReadings.bucket).getTime()); + lightSeries.data.push(groupedReadings.averageValue); + }); - lightSeries.data.push(groupedReadings.averageValue); + lightReady.value = true; }); + }; - lightReady.value = true; - }); + load(); - - - - - - - - - + { + timeSpan = value.timeSpan; + start = value.start; + end = value.end; + + load(); + } + " + @refresh="load"> + + + + + + + + + + + @@ -87,5 +119,10 @@ .container { height: 100%; background-color: #fafafa; + padding: 0; + } + + .content { + padding: 10px; }