diff --git a/src/components/eco-chart.ts b/src/components/eco-chart.ts new file mode 100644 index 0000000..dd2d891 --- /dev/null +++ b/src/components/eco-chart.ts @@ -0,0 +1,138 @@ +import { LitElement, html, TemplateResult, css } from 'lit'; +import { customElement, property, state } from 'lit/decorators.js'; + +// Third-party Libraries +import ApexCharts from 'apexcharts'; + +@customElement('eco-chart') +export class EcoChart extends LitElement { + @property({ type: Object }) ecoData: { + bonusRange: number; + acceleration: number; + constant: number; + freeWheel: number; + } = { bonusRange: 0, acceleration: 0, constant: 0, freeWheel: 0 }; + + @state() private chart: ApexCharts | undefined; + + private options = { + series: [0, 0, 0], // Dummy data to initialize the chart + chart: { + height: 350, + width: 350, + type: 'radialBar', + }, + plotOptions: { + radialBar: { + offsetY: 0, + startAngle: 0, + endAngle: 270, + hollow: { + margin: 5, + size: '40%', + background: '#ffffff', + image: undefined, + }, + dataLabels: { + textAnchor: 'middle', + distributed: false, + name: { + show: true, + }, + value: { + show: true, + fontSize: '24px', + fontWeight: 'bold', + }, + total: { + show: true, + label: 'Bonus range', + formatter: () => { + return `${this.ecoData.bonusRange || 0} km`; + }, + offsetX: 50, + offsetY: 10, + }, + }, + barLabels: { + enabled: true, + useSeriesColors: true, + margin: 8, + fontSize: '16px', + formatter: (seriesName: string, opts: any) => { + return `${seriesName}: ${opts.w.globals.series[opts.seriesIndex]}`; + }, + }, + }, + }, + colors: ['#1ab7ea', '#0084ff', '#39539E'], + labels: ['Acceleration', 'Constant', 'Free wheel'], + responsive: [ + { + breakpoint: 480, + options: { + legend: { + show: false, + }, + }, + }, + ], + }; + + protected firstUpdated() { + this.chart = new ApexCharts(this.shadowRoot?.getElementById('chart'), this.options); + this.chart.render(); + } + + updated(changedProperties: Map) { + if (changedProperties.has('ecoData')) { + this.updateChart(); + } + } + + private updateChart() { + if (this.chart) { + this.chart.updateOptions({ + series: [this.ecoData.acceleration, this.ecoData.constant, this.ecoData.freeWheel], + plotOptions: { + radialBar: { + dataLabels: { + total: { + formatter: () => { + return `${this.ecoData.bonusRange || 0} km`; + }, + }, + }, + }, + }, + }); + } + } + + protected render(): TemplateResult { + return html`
`; + } + + static styles = css` + #chart { + display: flex; + justify-content: center; + position: relative; + width: 100%; + max-height: 350px; + margin: 0; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, + 'Open Sans', 'Helvetica Neue', sans-serif !important; + } + .apexcharts-datalabels-group .apexcharts-text { + font-size: 1.2rem; + fill: var(--primary-text-color); + } + .apexcharts-radialbar-track > path { + stroke: var(--divider-color); + } + .apexcharts-radialbar-hollow { + fill: var(--ha-card-background, var(--card-background-color, #fff)); + } + `; +} diff --git a/src/css/styles.css b/src/css/styles.css index 332c5aa..9029920 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -362,27 +362,6 @@ header h1 { color: var(--error-color); } -#chart { - display: flex; - justify-content: center; - position: relative; - width: 100%; - max-height: 350px; - margin: 0; - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', - 'Helvetica Neue', sans-serif !important; -} -.apexcharts-datalabels-group .apexcharts-text { - font-size: 1.2rem; - fill: var(--primary-text-color); -} -.apexcharts-radialbar-track > path { - stroke: var(--divider-color); -} -.apexcharts-radialbar-hollow { - fill: var(--ha-card-background, var(--card-background-color, #fff)); -} - dialog { width: 100%; overflow: hidden; diff --git a/src/types.ts b/src/types.ts index 92c62a5..97512d7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -94,7 +94,7 @@ export interface EntityConfig { /** * Filters for binary sensors. */ -export const binarySensorsFilters: { +const binarySensorsFilters: { [key: string]: { prefix?: string; suffix: string }; } = { lock: { prefix: 'lock', suffix: '_lock' }, @@ -114,7 +114,7 @@ export const binarySensorsFilters: { /** * Filters for sensor devices. */ -export const sensorDeviceFilters: { +const sensorDeviceFilters: { [key: string]: { prefix?: string; suffix: string }; } = { lockSensor: { prefix: 'sensor.', suffix: '_lock' }, diff --git a/src/vehicle-info-card.ts b/src/vehicle-info-card.ts index 7dc63c1..3324970 100644 --- a/src/vehicle-info-card.ts +++ b/src/vehicle-info-card.ts @@ -30,7 +30,7 @@ import styles from './css/styles.css'; import { amgBlack, amgWhite } from './utils/imgconst'; import './components/map-card.js'; import './components/header-slide.js'; - +import './components/eco-chart'; declare global { interface Window { customCards: any[]; @@ -350,6 +350,19 @@ export class VehicleCard extends LitElement { `; } + private _renderEcoChart(): TemplateResult | void { + if (this.activeCardType !== 'ecoCards') return html``; + + const ecoData = { + bonusRange: parseFloat(this.getEntityState(this.vehicleEntities.ecoScoreBonusRange?.entity_id)) || 0, + acceleration: parseFloat(this.getEntityState(this.vehicleEntities.ecoScoreAcceleraion?.entity_id)) || 0, + constant: parseFloat(this.getEntityState(this.vehicleEntities.ecoScoreConstant?.entity_id)) || 0, + freeWheel: parseFloat(this.getEntityState(this.vehicleEntities.ecoScoreFreeWheel?.entity_id)) || 0, + }; + + return html``; + } + private _renderButtons(): TemplateResult { if (!this.config.show_buttons) return html``; @@ -755,7 +768,7 @@ export class VehicleCard extends LitElement { return html`
Eco display
-
+ ${this._renderEcoChart()}
${this.createItemDataRow('Scores', ecoData)}`; }