Builds system load usage widget

This commit is contained in:
Alicia Sykes 2021-12-15 02:28:48 +00:00
parent 283d8c750e
commit f1bc2a2888
3 changed files with 175 additions and 0 deletions

View File

@ -17,6 +17,8 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [Joke of the Day](#joke)
- [Flight Data](#flight-data)
- [Self-Hosted Services Widgets](#dynamic-widgets)
- [CPU History](#cpu-history-netdata)
- [System Load History](#load-history-netdata)
- [Dynamic Widgets](#dynamic-widgets)
- [Iframe Widget](#iframe-widget)
- [HTML Embed Widget](#html-embedded-widget)
@ -388,6 +390,26 @@ Pull recent CPU usage history from NetData.
host: http://192.168.1.1:19999
```
### Load History (NetData)
Pull recent load usage in 1, 5 and 15 minute intervals, from NetData.
<p align="center"><img width="400" src="https://i.ibb.co/qR9C2tJ/nd-load-history.png" /></p>
##### Options
**Field** | **Type** | **Required** | **Description**
--- | --- | --- | ---
**`host`** | `string` | Required | The URL to your NetData instance
##### Example
```yaml
- type: nd-load-history
options:
host: http://192.168.1.1:19999
```
---
## Dynamic Widgets

View File

@ -0,0 +1,144 @@
<template>
<div class="load-history-chart" :id="chartId"></div>
</template>
<script>
import axios from 'axios';
import { Chart } from 'frappe-charts/dist/frappe-charts.min.esm';
import WidgetMixin from '@/mixins/WidgetMixin';
export default {
mixins: [WidgetMixin],
components: {},
data() {
return {
chartTitle: null,
chartData: null,
chartDom: null,
};
},
mounted() {
this.fetchData();
},
computed: {
/* URL where NetData is hosted */
netDataHost() {
const usersChoice = this.options.host;
if (!usersChoice || typeof usersChoice !== 'string') {
this.error('Host parameter is required');
return '';
}
return usersChoice;
},
apiVersion() {
return this.options.apiVersion || 'v1';
},
endpoint() {
return `${this.netDataHost}/api/${this.apiVersion}/data?chart=system.cpu`;
},
/* A sudo-random ID for the chart DOM element */
chartId() {
return `cpu-history-chart-${Math.round(Math.random() * 10000)}`;
},
chartHeight() {
return this.options.chartHeight || 300;
},
getChartColor() {
const cssVars = getComputedStyle(document.documentElement);
return cssVars.getPropertyValue('--widget-text-color').trim() || '#7cd6fd';
},
},
methods: {
/* Make GET request to NetData */
fetchData() {
axios.get(this.endpoint)
.then((response) => {
this.processData(response.data);
})
.catch((dataFetchError) => {
this.error('Unable to fetch data', dataFetchError);
})
.finally(() => {
this.finishLoading();
});
},
/* Assign data variables to the returned data */
processData(data) {
const timeData = [];
const load1min = [];
const load5mins = [];
const load15mins = [];
data.data.reverse().forEach((reading) => {
timeData.push(this.formatDate(reading[0]));
load1min.push(reading[1]);
load5mins.push(reading[2]);
load15mins.push(reading[3]);
});
this.chartData = {
labels: timeData,
datasets: [
{ name: '1 Min', type: 'bar', values: load1min },
{ name: '5 Mins', type: 'bar', values: load5mins },
{ name: '15 Mins', type: 'bar', values: load15mins },
],
};
this.chartTitle = this.makeChartTitle(data.data);
this.renderChart();
},
makeChartTitle(data) {
if (!data || !data[0][0]) return '';
const diff = Math.round((data[data.length - 1][0] - data[0][0]) / 60);
return `Past ${diff} minutes`;
},
renderChart() {
this.chartDom = this.generateChart();
},
/* Create new chart, using the crypto data */
generateChart() {
return new Chart(`#${this.chartId}`, {
title: this.chartTitle,
data: this.chartData,
type: 'axis-mixed',
height: this.chartHeight,
colors: [this.getChartColor, '#743ee2'],
truncateLegends: true,
lineOptions: {
regionFill: 1,
hideDots: 1,
},
axisOptions: {
xIsSeries: true,
xAxisMode: 'tick',
},
tooltipOptions: {
formatTooltipY: d => `${Math.round(d)}%`,
},
});
},
/* Format the date for a given time stamp, also include time if required */
formatDate(timestamp) {
const localFormat = navigator.language;
const dateFormat = { weekday: 'short', day: 'numeric', month: 'short' };
const timeFormat = { hour: 'numeric', minute: 'numeric', second: 'numeric' };
const date = new Date(timestamp * 1000).toLocaleDateString(localFormat, dateFormat);
const time = Intl.DateTimeFormat(localFormat, timeFormat).format(timestamp);
return `${date} ${time}`;
},
},
};
</script>
<style lang="scss">
.load-history-chart .chart-container {
text.title {
text-transform: capitalize;
color: var(--widget-text-color);
}
.axis, .chart-label {
fill: var(--widget-text-color);
opacity: var(--dimming-factor);
&:hover { opacity: 1; }
}
}
</style>

View File

@ -109,6 +109,13 @@
@error="handleError"
:ref="widgetRef"
/>
<NdLoadHistory
v-else-if="widgetType === 'nd-load-history'"
:options="widgetOptions"
@loading="setLoaderState"
@error="handleError"
:ref="widgetRef"
/>
<IframeWidget
v-else-if="widgetType === 'iframe'"
:options="widgetOptions"
@ -151,6 +158,7 @@ import StockPriceChart from '@/components/Widgets/StockPriceChart.vue';
import Jokes from '@/components/Widgets/Jokes.vue';
import Flights from '@/components/Widgets/Flights.vue';
import NdCpuHistory from '@/components/Widgets/NdCpuHistory.vue';
import NdLoadHistory from '@/components/Widgets/NdLoadHistory.vue';
import IframeWidget from '@/components/Widgets/IframeWidget.vue';
import EmbedWidget from '@/components/Widgets/EmbedWidget.vue';
@ -174,6 +182,7 @@ export default {
Jokes,
Flights,
NdCpuHistory,
NdLoadHistory,
IframeWidget,
EmbedWidget,
},