integrasi dashboard energy monitoring

This commit is contained in:
Fuzi_fauzia 2024-06-06 14:54:14 +07:00
parent 3f1c97ebe4
commit 38c936b389
7 changed files with 1223 additions and 906 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
:host ::ng-deep .donut-chart1 .ct-series-a .ct-slice-donut {
stroke: green;
stroke-width: 50px !important;
}
:host ::ng-deep .donut-chart1 .ct-series-b .ct-slice-donut {
stroke: yellow;
stroke-width: 50px !important;
}
:host ::ng-deep .donut-chart1 .ct-series-c .ct-slice-donut {
stroke: orange;
stroke-width: 50px !important;
}
:host ::ng-deep .donut-chart1 .ct-series-d .ct-slice-donut {
stroke: red;
stroke-width: 50px !important;
}
:host ::ng-deep .donut-chart1 .ct-series-e .ct-slice-donut {
stroke: darkred;
stroke-width: 50px !important;
}
:host ::ng-deep .donut-chart2 .ct-series-a .ct-slice-donut {
stroke: #8a8a8a;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 .ct-series-b .ct-slice-donut {
stroke: #bef264;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 {
-webkit-filter: drop-shadow(0px 10px 11px rgba(187, 187, 187)) !important;
filter: drop-shadow(0px 10px 11px rgba(187, 187, 187));
}
:host ::ng-deep .donut-chart2 .ct-label {
fill: #111010;
color: rgba(0, 0, 0, 0.4);
font-size: 1.75rem;
line-height: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-point {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-line {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-area {
fill: #bef264;
fill-opacity: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-point {
stroke-width: 0px;
}

View File

@ -1,33 +1,108 @@
<div class="app-content content bg-maintenance-image">
<div class="app-content content">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section class="flexbox-container">
<div class="col-12 d-flex align-items-center justify-content-center">
<div class="col-md-4 col-10 box-shadow-2 p-0">
<div class="card border-grey border-lighten-3 px-1 py-1 box-shadow-3 m-0">
<section>
<div class="row">
<div class="col-lg-4 col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title text-center">Temperature and Humidity</h4>
</div>
<div class="card-body">
<span class="card-title text-center">
<img src="../../../../assets/images/logo/logo-dark-lg.png" class="img-fluid mx-auto d-block pt-2"
width="250" alt="logo">
</span>
<div class="card-block">
<div class="donut-chart2" style="height: 150px !important">
<x-chartist
*ngIf="donutChart2"
[data]="donutChart2.data"
[type]="donutChart2.type"
[options]="donutChart2.options"
[responsiveOptions]="donutChart2.responsiveOptions"
[events]="donutChart2.events"
></x-chartist>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title text-center">Air Quality</h4>
</div>
<div class="card-body">
<div class="card-block">
<div class="donut-chart1" style="height: 150px !important">
<x-chartist
*ngIf="donutChart1"
[data]="donutChart1.data"
[type]="donutChart1.type"
[options]="donutChart1.options"
[responsiveOptions]="donutChart1.responsiveOptions"
[events]="donutChart1.events"
></x-chartist>
</div>
<div
class="text-center"
style="
position: absolute;
top: 80%;
left: 50%;
transform: translate(-50%, -50%);
"
>
<h3
class="display-4 blue-grey darken-1"
style="font-size: 2em"
>
76 %
</h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title text-center">Comparison of Actual Costs and Estimated Costs</h4>
</div>
<div class="card-body">
<div class="card-block">
<div class="d-flex align-items-center mb-2">
<div class="flex-grow-1">
<ngb-progressbar
height="45px"
type="danger"
[value]="89"
></ngb-progressbar>
</div>
<div
class="ml-2 d-flex align-items-center"
style="height: 20px"
>
<span class="text-bold-600">1.234.242</span>
</div>
</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<ngb-progressbar
height="45px"
type="success"
[value]="50"
></ngb-progressbar>
</div>
<div
class="ml-2 d-flex align-items-center"
style="height: 20px"
>
<span class="text-bold-600">1.545.232</span>
</div>
</div>
</div>
<div class="card-body text-center">
<h3>Coming Soon</h3>
<p>We're sorry for the inconvenience.
<br> Please check back later.</p>
<div class="mt-2"><i class="la la-cog spinner font-large-2"></i></div>
</div>
<hr>
<!-- <p class="socialIcon card-text text-center pt-2 pb-2">
<a [routerLink]="" class="btn btn-social-icon mr-1 mb-1 btn-outline-facebook"><span
class="la la-facebook"></span></a>
<a [routerLink]="" class="btn btn-social-icon mr-1 mb-1 btn-outline-twitter"><span
class="la la-twitter"></span></a>
<a [routerLink]="" class="btn btn-social-icon mr-1 mb-1 btn-outline-linkedin"><span
class="la la-linkedin font-medium-4"></span></a>
<a [routerLink]="" class="btn btn-social-icon mr-1 mb-1 btn-outline-github"><span
class="la la-github font-medium-4"></span></a>
</p> -->
</div>
</div>
</div>

View File

@ -14,6 +14,21 @@ export class CostManagementComponent implements OnInit{
searchTerm: string = "";
rows: any = [];
donutChart1: any;
donutChart2: any;
dataChart = {
donut: {
series: [50, 50],
labels: [],
},
};
dataChart2 = {
donut: {
series: [50, 50],
labels: [],
},
};
barChart: any;
dataBar = {
"Bar": {
@ -65,6 +80,50 @@ export class CostManagementComponent implements OnInit{
}
}
};
this.donutChart1 = {
type: "Pie",
data: this.dataChart.donut,
options: {
fullwidth: true,
height: "300px",
donut: true,
donutWidth: 70,
startAngle: 270,
total: 200,
showLabel: true,
},
};
this.donutChart2 = {
type: "Pie",
data: this.dataChart2.donut,
options: {
chartPadding: 0,
fullwidth: true,
height: "150px",
donut: true,
showLabel: true,
startAngle: 0,
labelInterpolationFnc: function (value) {
const total = 82;
return total + "C";
},
},
events: {
draw(data: any): void {
if (data.type === "label") {
if (data.index === 0) {
data.element.attr({
dx: data.element.root().width() / 2,
dy: data.element.root().height() / 2,
});
} else {
data.element.remove();
}
}
},
},
};
}
fetchData() {

View File

@ -12,7 +12,7 @@
<div class="media d-flex">
<div class="media-body text-left">
<h5>Electricity</h5>
<h3 class="danger">278 Kwh</h3>
<h3 class="danger">{{ topCard?.kwh_consumption }} Kwh</h3>
</div>
<div class="align-self-center">
<i class="icon-energy danger font-large-2 float-right"></i>
@ -29,7 +29,7 @@
<div class="media d-flex">
<div class="media-body text-left">
<h5>Water</h5>
<h3 class="success">156</h3>
<h3 class="success">{{ topCard?.water_consumption }}</h3>
</div>
<div class="align-self-center">
<i
@ -48,7 +48,7 @@
<div class="media d-flex">
<div class="media-body text-left">
<h5>Device</h5>
<h3 class="warning">{{ device }}</h3>
<h3 class="warning">{{ topCard?.total_device }}</h3>
</div>
<div class="align-self-center">
<i class="icon-bulb warning font-large-2 float-right"></i>
@ -65,7 +65,7 @@
<div class="media d-flex">
<div class="media-body text-left">
<h5>Room</h5>
<h3 class="info">{{ room }}</h3>
<h3 class="info">{{ topCard?.total_room }}</h3>
</div>
<div class="align-self-center">
<i class="icon-map info font-large-2 float-right"></i>
@ -82,10 +82,7 @@
<section id="chartjs-bar-charts">
<div class="row">
<div class="col-12" *blockUI="'barCharts'; message: 'Loading'">
<m-card
[options]="options"
(reloadFunction)="reloadBarCharts($event)"
>
<m-card [options]="options">
<ng-container mCardHeaderTitle> Appointment </ng-container>
<ng-container mCardBody>
<div class="z" style="display: block">
@ -113,12 +110,17 @@
<div class="col-4">
<div class="card">
<div class="card-header">
<h4 class="card-title text-center">Air Quality</h4>
<h4 class="card-title text-center">Summary Cost</h4>
</div>
<div class="card-body">
<div class="card-block">
<div class="text-center" style="height: 150px !important">
<h2 class="font-large-3 text-bold-400">Rp 1.253.634.234</h2>
<h2 class="font-large-3 text-bold-400">
{{
summaryCost?.summary_cost
| currency : "Rp " : "symbol" : "1.0-0"
}}
</h2>
<p class="blue-grey lighten-2 mb-0 mt-2">
Based on the estimated costs you have
</p>
@ -158,7 +160,7 @@
class="display-4 blue-grey darken-1"
style="font-size: 2em"
>
76 %
{{ airQuality }}
</h3>
</div>
</div>
@ -189,81 +191,27 @@
</div>
</div>
<div class="row">
<div class="col-6">
<div class="col-6" *ngFor="let item of deviceCategory?.usesd">
<div class="card overflow-hidden">
<div class="card-content">
<div class="card-body cleartfix">
<div class="media align-items-stretch mb-2">
<div class="align-self-center">
<i class="icon-energy background-round info font-large-2 mr-2"></i>
<i
class="icon-bulb background-round info font-large-2 mr-2"
></i>
</div>
<div class="media-body">
<p>
General Lighting
<span class="float-right text-bold-600">89%</span>
{{ item.category_device }}
<span class="float-right text-bold-600"
>{{ item.value }}%</span
>
</p>
<ngb-progressbar
height="7px"
type="danger"
[value]="90"
></ngb-progressbar>
</div>
</div>
<div class="media align-items-stretch mt-2">
<div class="align-self-center">
<i class="icon-energy background-round info font-large-2 mr-2"></i>
</div>
<div class="media-body">
<p>
Accent Lighting
<span class="float-right text-bold-600">89%</span>
</p>
<ngb-progressbar
height="7px"
type="danger"
[value]="90"
></ngb-progressbar>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-6">
<div class="card overflow-hidden">
<div class="card-content">
<div class="card-body cleartfix">
<div class="media align-items-stretch mb-2">
<div class="align-self-center">
<i class="icon-layers background-round info font-large-2 mr-2"></i>
</div>
<div class="media-body">
<p>
Floor Lighting
<span class="float-right text-bold-600">89%</span>
</p>
<ngb-progressbar
height="7px"
type="danger"
[value]="90"
></ngb-progressbar>
</div>
</div>
<div class="media align-items-stretch mt-2">
<div class="align-self-center">
<i class="icon-bulb background-round info font-large-2 mr-2"></i>
</div>
<div class="media-body">
<p>
Last Lighting
<span class="float-right text-bold-600">89%</span>
</p>
<ngb-progressbar
height="7px"
type="danger"
[value]="90"
[value]="item.value"
></ngb-progressbar>
</div>
</div>

View File

@ -1,14 +1,14 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { BuildingService } from "../../service/monitoring-api.service";
import * as chartsData from "./data";
import * as Chartist from 'chartist';
import { Component, ElementRef, ViewChild } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ChartOptions, ChartType, ChartDataset } from "chart.js";
import { EnergyMonitoringService } from "../../service/energy-monitoring.service";
import { CurrencyPipe } from "@angular/common";
@Component({
selector: "app-detail",
templateUrl: "./detail.component.html",
styleUrls: ["./detail.component.css"],
providers: [CurrencyPipe],
})
export class DetailComponent {
data: any;
@ -16,21 +16,18 @@ export class DetailComponent {
breadcrumb: any;
donutChart1: any;
donutChart2: any;
sparklineArea: any
dataChart = {
"donut": {
"series": [20, 20, 20, 20, 20],
"labels": []
}
}
donut: {
series: [20, 20, 20, 20, 20],
},
};
dataChart2 = {
"donut": {
"series": [50, 50],
"labels": []
}
}
donut: {
series: [50, 50],
},
};
// chart bar
public barChartOptions: ChartOptions = {
@ -41,13 +38,14 @@ export class DetailComponent {
},
y: {
stacked: true,
beginAtZero: true
},
},
};
public barChartLabels: string[] = [];
public barChartType: ChartType = "bar";
public barChartLegend = true;
public barChartData: ChartDataset[] = [
{
data: [],
@ -74,9 +72,9 @@ export class DetailComponent {
categoryPercentage: 0.5,
},
{
type: "line", // override the default type
type: "line",
data: [],
label: "Appointment",
label: "Weekly Kwh Average",
backgroundColor: "rgba(0,255,255,0)",
borderColor: "#1e9ff2",
fill: false,
@ -85,6 +83,23 @@ export class DetailComponent {
pointBorderWidth: 2,
pointHoverBorderWidth: 2,
pointRadius: 4,
tension: 0.3,
spanGaps: true
},
{
type: "line",
data: [],
label: "Weekly Water Average",
backgroundColor: "rgba(0,255,255,0)",
borderColor: "#1e9ff2",
fill: false,
pointBorderColor: "#1e9ff2",
pointBackgroundColor: "#FFF",
pointBorderWidth: 2,
pointHoverBorderWidth: 2,
pointRadius: 4,
tension: 0.3,
spanGaps: true
},
];
//..........................
@ -94,34 +109,52 @@ export class DetailComponent {
water: any;
device: any;
room: any;
topCard: any;
summaryCost: any;
airQuality: any;
temperature: any;
deviceCategory: any;
chartKwhWater: any;
//......
constructor(
private router: Router,
private route: ActivatedRoute,
private monitoringApiService: BuildingService
private energyMonitoringService: EnergyMonitoringService,
private currencyPipe: CurrencyPipe
) {}
get formattedSummaryCost(): string {
return this.currencyPipe.transform(
this.summaryCost.summary_cost,
"IDR",
"symbol",
"1.0-0"
);
}
ngOnInit() {
this.route.data.subscribe((data) => {
this.mode = data.mode;
});
this.breadcrumbLink();
this.generateChartLabelsAndData();
this.route.params.subscribe((params) => {
const buildingId = params["id"];
if (buildingId) {
this.fetchData(buildingId);
this.devicePerBuilding(buildingId);
this.dataEnergyMonitoringTopCard(buildingId);
this.dataEnergyMonitoringSummary(buildingId);
this.dataEnergyMonitoringSAir(buildingId);
this.dataEnergyMonitoringSTemperature(buildingId);
this.dataEnergyDeviceCategory(buildingId);
this.dataEnergyChartKwhWater(buildingId);
}
});
this.donutChart1 = {
type: 'Pie',
type: "Pie",
data: this.dataChart.donut,
options: {
fullwidth: true,
height: '300px',
height: "300px",
donut: true,
donutWidth: 70,
startAngle: 270,
@ -129,37 +162,6 @@ export class DetailComponent {
showLabel: true,
},
};
this.donutChart2 = {
type: 'Pie',
data: this.dataChart2.donut,
options: {
chartPadding: 0,
fullwidth: true,
height: '150px',
donut: true,
showLabel: true,
startAngle: 0,
labelInterpolationFnc: function (value) {
const total = 82;
return total + 'C';
}
},
events: {
draw(data: any): void {
if (data.type === 'label') {
if (data.index === 0) {
data.element.attr({
dx: data.element.root().width() / 2,
dy: data.element.root().height() / 2
});
} else {
data.element.remove();
}
}
}
}
};
}
breadcrumbLink() {
@ -201,41 +203,140 @@ export class DetailComponent {
};
}
}
generateChartLabelsAndData() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const daysInMonth = new Date(year, month + 1, 0).getDate();
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(year, month, day);
this.barChartLabels.push(date.toISOString().split("T")[0]); // Format YYYY-MM-DD
// Example data generation, replace with your actual data logic
this.barChartData[0].data?.push(Math.floor(Math.random() * 100));
this.barChartData[1].data?.push(Math.floor(Math.random() * 100));
this.barChartData[2].data?.push(Math.floor(Math.random() * 100));
}
}
//integrasi
fetchData(buildingId) {
this.monitoringApiService
.getRoomByBuildingId(buildingId)
dataEnergyMonitoringTopCard(buildingId) {
this.energyMonitoringService
.getDashboardTopCard(buildingId)
.subscribe((res) => {
this.room = res.resp.map((entry) => entry.roomEntity).length;
this.topCard = res.data;
});
}
dataEnergyMonitoringSummary(buildingId) {
this.energyMonitoringService
.getDashboardSummary(buildingId)
.subscribe((res) => {
this.summaryCost = res.data;
});
}
dataEnergyMonitoringSAir(buildingId) {
this.energyMonitoringService
.getDashboardAirQuality(buildingId)
.subscribe((res) => {
this.airQuality = res.data;
});
}
dataEnergyMonitoringSTemperature(buildingId) {
this.energyMonitoringService
.getDashboardTemperature(buildingId)
.subscribe((res) => {
this.temperature = res.data;
this.donutChart2 = {
type: "Pie",
data: this.dataChart2.donut,
options: {
chartPadding: 0,
fullwidth: true,
height: "150px",
donut: true,
showLabel: true,
startAngle: 0,
labelInterpolationFnc: (value) => {
const total = this.temperature;
return total + "°C";
},
},
events: {
draw: (data: any) => {
if (data.type === "label") {
if (data.index === 0) {
data.element.attr({
dx: data.element.root().width() / 2,
dy: data.element.root().height() / 2,
});
} else {
data.element.remove();
}
}
},
},
};
});
}
dataEnergyDeviceCategory(buildingId) {
this.energyMonitoringService
.getDashboardDeviceCategory(buildingId)
.subscribe((res) => {
this.deviceCategory = res.data[0];
});
}
dataEnergyChartKwhWater(buildingId) {
this.energyMonitoringService
.getDashboardChartKwhWater(buildingId)
.subscribe((res) => {
this.chartKwhWater = res.data;
this.barChartData[0].data = [];
this.barChartData[1].data = [];
this.chartKwhWater.forEach((entry) => {
this.barChartData[0].data.push(entry.kwh);
this.barChartData[1].data.push(entry.water);
this.barChartLabels.push(entry.day);
});
const weeklyKwh = this.aggregateKwhWeekly(this.chartKwhWater);
const weeklyWater = this.aggregateWaterWeekly(this.chartKwhWater);
// Populate Appointment data at every multiple of 7
let weekIndex = 0;
for (let i = 0; i < this.barChartData[0].data.length; i++) {
if (i % 7 === 6 && weekIndex < weeklyKwh.length) {
this.barChartData[2].data[i] = weeklyKwh[weekIndex++];
} else {
this.barChartData[2].data[i] = null;
}
}
devicePerBuilding(buildingId) {
this.monitoringApiService
.listDevicePerBuilding(buildingId)
.subscribe((res) => {
this.device = res.resp.reduce(
(sum, item) => sum + item.total_device,
0
);
let weekIndexWater = 0;
for (let i = 0; i < this.barChartData[0].data.length; i++) {
if (i % 7 === 6 && weekIndexWater < weeklyWater.length) {
this.barChartData[3].data[i] = weeklyWater[weekIndexWater++];
} else {
this.barChartData[3].data[i] = null;
}
}
});
}
aggregateKwhWeekly(data: any[]): number[] {
const weeks = [];
let weekSum = 0;
let dayCount = 0;
data.forEach((entry, index) => {
weekSum += entry.kwh;
dayCount++;
if (dayCount === 7 || index === data.length - 1) {
weeks.push(weekSum);
weekSum = 0;
dayCount = 0;
}
});
return weeks;
}
aggregateWaterWeekly(data: any[]): number[] {
const weeks = [];
let weekSum = 0;
let dayCount = 0;
data.forEach((entry, index) => {
weekSum += entry.water;
dayCount++;
if (dayCount === 7 || index === data.length - 1) {
weeks.push(weekSum);
weekSum = 0;
dayCount = 0;
}
});
return weeks;
}
}

View File

@ -0,0 +1,71 @@
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from "rxjs";
@Injectable({
providedIn: "root",
})
export class EnergyMonitoringService {
constructor(private http: HttpClient) {}
getListRoomData(): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/room/list`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardTopCard(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/top-card?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardSummary(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/summary-cost?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardAirQuality(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/air-quality?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardTemperature(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/temperature-humidity?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardDeviceCategory(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/device-category-use?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
getDashboardChartKwhWater(id): Observable<any> {
const url = `https://kapi.absys.ninja/hemat/dashboard/chartKwhWater?building_id=${id}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'x-api-key': 'j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT'
});
return this.http.get<any>(url, { headers });
}
}