diff --git a/package-lock.json b/package-lock.json index 17c2478..2d50301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", "@coreui/coreui": "^5.1.2", + "apexcharts": "^3.53.0", "bootstrap": "^5.3.3", "ngx-toastr": "^19.0.0", "rxjs": "~7.8.0", @@ -4261,6 +4262,11 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/@yr/monotone-cubic-spline": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz", + "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==" + }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -4501,6 +4507,20 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/apexcharts": { + "version": "3.53.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.53.0.tgz", + "integrity": "sha512-QESZHZY3w9LPQ64PGh1gEdfjYjJ5Jp+Dfy0D/CLjsLOPTpXzdxwlNMqRj+vPbTcP0nAHgjWv1maDqcEq6u5olw==", + "dependencies": { + "@yr/monotone-cubic-spline": "^1.0.3", + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -11985,6 +12005,89 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 4e52b19..033e413 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", "@coreui/coreui": "^5.1.2", + "apexcharts": "^3.53.0", "bootstrap": "^5.3.3", "ngx-toastr": "^19.0.0", "rxjs": "~7.8.0", diff --git a/src/app/views/energy-overview/energy-overview.component.html b/src/app/views/energy-overview/energy-overview.component.html index 9535755..843d174 100644 --- a/src/app/views/energy-overview/energy-overview.component.html +++ b/src/app/views/energy-overview/energy-overview.component.html @@ -1,26 +1,34 @@ -
+

Welcome to the Energy Data Component!

This is a simple Angular component.

-
-
- - - - - - - - - - - - - - - -
BatterielevelBatterie ProzentBatterie SpannungBatterie StatusZeitpunkt
{{ data.batteryLevel }}{{ data.batteryPercent }}{{ data.batteryVoltage }}{{ data.batteryStatus }}{{ data.occuredAt | date }}
+
+
+
+

Batterie Level Graph

+
+
+
chart
+
+
+ + + + + + + + + + + + + + + +
BatterielevelBatterie ProzentBatterie SpannungBatterie StatusZeitpunkt
{{ data.batteryLevel }}{{ data.batteryPercent }}{{ data.batteryVoltage }}{{ data.batteryStatus }}{{ data.occuredAt | date }}
+
diff --git a/src/app/views/energy-overview/energy-overview.component.ts b/src/app/views/energy-overview/energy-overview.component.ts index 7c747f9..3848f9c 100644 --- a/src/app/views/energy-overview/energy-overview.component.ts +++ b/src/app/views/energy-overview/energy-overview.component.ts @@ -1,23 +1,105 @@ -import { Component, Inject, OnInit } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + Inject, + OnInit, + ViewChild, +} from '@angular/core'; import { EnergyDataService } from '../../services/energy-data.service'; +import ApexCharts, { ApexOptions } from 'apexcharts'; @Component({ selector: 'app-energy-overview', templateUrl: './energy-overview.component.html', styleUrls: ['./energy-overview.component.scss'], }) -export class EnergyOverviewComponent implements OnInit { +export class EnergyOverviewComponent implements OnInit, AfterViewInit { constructor( @Inject(EnergyDataService) private readonly energyService: EnergyDataService ) {} + @ViewChild('batteryChart') chart?: ElementRef; energyData: any; + mockDate = new Date(); mockData = [ + // { + // batteryLevel: 40, + // batteryPercent: '50%', + // batteryStatus: 'Charging', + // batteryVoltage: '12.5', + // occuredAt: this.mockDate, + // }, + { + batteryLevel: 10, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date( + this.mockDate.setHours(this.mockDate.getHours() - 10) + ), + }, + { + batteryLevel: 15, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 9)), + }, + { + batteryLevel: 33, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 8)), + }, + { + batteryLevel: 25, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 7)), + }, { batteryLevel: 50, batteryPercent: '50%', batteryStatus: 'Charging', batteryVoltage: '12.5', - occuredAt: new Date(), + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 6)), + }, + { + batteryLevel: 64, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 5)), + }, + { + batteryLevel: 80, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 4)), + }, + { + batteryLevel: 100, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 3)), + }, + { + batteryLevel: 78, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 2)), + }, + { + batteryLevel: 54, + batteryPercent: '50%', + batteryStatus: 'Charging', + batteryVoltage: '12.5', + occuredAt: new Date(this.mockDate.setHours(this.mockDate.getHours() - 1)), }, ]; ngOnInit(): void { @@ -25,4 +107,74 @@ export class EnergyOverviewComponent implements OnInit { this.energyData = data; }); } + + ngAfterViewInit() { + this.drawChart(); + } + + drawChart(force = false) { + // if (!this.mockData) return; + // if (this.chart?.nativeElement.hasChildNodes()) { + // if (force) this.chart.nativeElement.innerHTML = ''; + // else return; + // } + const values: { x: number; y: number }[] = []; + if (this.mockData) + this.mockData.forEach((h: any) => { + const value = h.batteryLevel; + const timestamp = new Date(h.occuredAt).getTime(); + values.unshift({ x: timestamp, y: value }); + }); + const options: ApexOptions = { + series: [ + { + name: 'Ladestatus in %', + data: values, + }, + ], + chart: { + id: 'area-datetime', + type: 'area', + height: 400, + zoom: { + enabled: true, + }, + animations: { + enabled: false, + }, + }, + stroke: { + curve: 'smooth', + }, + dataLabels: { + enabled: false, + }, + xaxis: { + type: 'datetime', + labels: { + datetimeUTC: false, + }, + }, + yaxis: { + min: 0, + max: 100, + }, + tooltip: { + x: { + format: 'dd MMM yyyy, HH mm', + }, + }, + fill: { + type: 'gradient', + gradient: { + shadeIntensity: 1, + opacityFrom: 0.7, + opacityTo: 0.9, + stops: [0, 100], + }, + }, + colors: ['#4DBD74'], + }; + new ApexCharts(this.chart?.nativeElement, options).render(); + } }