741 lines
25 KiB
JavaScript
741 lines
25 KiB
JavaScript
const LitElement =
|
||
window.LitElement || Object.getPrototypeOf(customElements.get('home-assistant') || customElements.get('hui-view'));
|
||
const { html, css } = LitElement.prototype;
|
||
|
||
const locale = {
|
||
da: {
|
||
minPrice: 'Minimumspris i dag:',
|
||
maxPrice: 'Maksimal pris i dag:',
|
||
minPriceNextDay: 'Minimumspris i morgen:',
|
||
maxPriceNextDay: 'Maksimal pris i morgen:',
|
||
infoNoNextDay: 'Morgendagens data er endnu ikke tilgængelige',
|
||
from: 'fra',
|
||
to: 'til'
|
||
},
|
||
de: {
|
||
minPrice: 'Mindestpreis heute:',
|
||
maxPrice: 'Maximaler preis heute:',
|
||
minPriceNextDay: 'Mindestpreis morgen:',
|
||
maxPriceNextDay: 'Maximaler preis morgen:',
|
||
infoNoNextDay: 'Die Daten von morgen sind noch nicht verfügbar',
|
||
from: 'von',
|
||
to: 'bis'
|
||
},
|
||
en: {
|
||
minPrice: 'Lowest price today:',
|
||
maxPrice: 'Highest price today:',
|
||
minPriceNextDay: 'Lowest price tomorrow:',
|
||
maxPriceNextDay: 'Highest price tomorrow:',
|
||
infoNoNextDay: "Tomorrow's data is not yet available",
|
||
from: 'from',
|
||
to: 'to'
|
||
},
|
||
es: {
|
||
minPrice: 'Precio mínimo hoy:',
|
||
maxPrice: 'Precio máximo hoy:',
|
||
minPriceNextDay: 'Precio mínimo mañana:',
|
||
maxPriceNextDay: 'Precio máximo mañana:',
|
||
infoNoNextDay: 'Los datos de mañana no están disponibles aún',
|
||
from: 'de',
|
||
to: 'a'
|
||
},
|
||
fr: {
|
||
minPrice: "Prix minimum aujourd'hui:",
|
||
maxPrice: "Prix maximum aujourd'hui:",
|
||
minPriceNextDay: 'Prix minimum demain:',
|
||
maxPriceNextDay: 'Prix maximum demain:',
|
||
infoNoNextDay: 'Les données de demain ne sont pas encore disponibles',
|
||
from: 'de',
|
||
to: 'à'
|
||
},
|
||
nl: {
|
||
minPrice: 'Minimumspris i dag:',
|
||
maxPrice: 'Maksimal pris i dag:',
|
||
minPriceNextDay: 'Minimum prijs morgen:',
|
||
maxPriceNextDay: 'Maximale prijs morgen:',
|
||
infoNoNextDay: 'De gegevens van morgen zijn nog niet beschikbaar',
|
||
from: 'fra',
|
||
to: 'til'
|
||
},
|
||
ru: {
|
||
minPrice: 'Минимальная цена сегодня:',
|
||
maxPrice: 'Максимальная цена сегодня:',
|
||
minPriceNextDay: 'Минимальная цена завтра:',
|
||
maxPriceNextDay: 'Максимальная цена завтра:',
|
||
infoNoNextDay: 'Данные завтра еще не доступны',
|
||
from: 'С',
|
||
to: 'до'
|
||
},
|
||
sv: {
|
||
minPrice: 'Lägsta pris idag:',
|
||
maxPrice: 'Maxpris idag:',
|
||
minPriceNextDay: 'Lägsta pris imorgon:',
|
||
maxPriceNextDay: 'Maxpris i morgon:',
|
||
infoNoNextDay: 'Morgondagens data är ännu inte tillgängliga',
|
||
from: '',
|
||
to: 'till'
|
||
}
|
||
};
|
||
|
||
const tariffPeriodIconColors = {
|
||
error: '--error-color',
|
||
normal: '--warning-color',
|
||
peak: '--error-color',
|
||
valley: '--success-color',
|
||
'super-valley': '--info-color'
|
||
};
|
||
|
||
const tariffPeriodIcons = {
|
||
error:
|
||
'M 28.342306,10.429944 27.798557,32.995546 H 24.243272 L 23.657695,10.429944 Z M 28.133172,41.570057 H 23.86683 v -4.412736 h 4.266342 z',
|
||
normal:
|
||
'M 31.032172,16.612305 20.999855,32.113255 15.66609,25.065424 H 0.97821381 a 25.017275,25.017275 0 0 0 -0.0332829,0.949884 25.017275,25.017275 0 0 0 0.0468985,0.940092 H 14.800215 l 6.199595,8.453119 10.03232,-15.502917 5.335714,7.049798 h 14.578421 a 25.017275,25.017275 0 0 0 0.03328,-0.940092 25.017275,25.017275 0 0 0 -0.0469,-0.949884 H 37.233737 Z',
|
||
peak:
|
||
'M 2.5238392,34.768609 A 25.003164,25.003164 0 0 1 1.9104804,32.879664 h 8.6436716 l 15.49805,-22.870055 15.121052,22.870055 h 8.891749 a 25.003164,25.003164 0 0 1 -0.628986,1.888945 H 40.038344 L 26.052202,13.679995 12.06606,34.768609 Z',
|
||
valley:
|
||
'm 2.5238392,17.238401 a 25.003164,25.003164 0 0 0 -0.6133588,1.888945 h 8.6436716 l 15.49805,22.870055 15.121052,-22.870055 h 8.891749 A 25.003164,25.003164 0 0 0 49.436017,17.238401 H 40.038344 L 26.052202,38.327015 12.06606,17.238401 Z',
|
||
'super-valley':
|
||
'm 30.867213,27.342466 c 0,0.670334 -0.543413,1.213747 -1.213747,1.213746 -0.670333,-10e-7 -1.213744,-0.543413 -1.213744,-1.213746 0,-0.670333 0.543411,-1.213745 1.213744,-1.213746 0.670334,-1e-6 1.213747,0.543412 1.213747,1.213746 z m -7.282476,0 c 0,0.670333 -0.543412,1.213746 -1.213745,1.213746 -0.670334,0 -1.213746,-0.543412 -1.213746,-1.213746 0,-0.670334 0.543412,-1.213746 1.213746,-1.213746 0.670333,0 1.213745,0.543413 1.213745,1.213746 z m 8.026907,-6.869803 c -0.161832,-0.477407 -0.614966,-0.817256 -1.149013,-0.817256 h -8.900804 c -0.534048,0 -0.979088,0.339849 -1.149012,0.817256 l -1.683061,4.846893 v 6.473312 c 0,0.445039 0.364123,0.809164 0.809163,0.809164 h 0.809164 c 0.445041,0 0.809165,-0.364125 0.809165,-0.809164 v -0.809165 h 9.709967 v 0.809165 c 0,0.445039 0.364125,0.809164 0.809164,0.809164 h 0.809165 c 0.445039,0 0.809163,-0.364125 0.809163,-0.809164 v -6.473312 z m -9.800018,0.767664 h 8.393115 l 0.841531,2.49431 H 20.970096 Z m 9.89816,8.158458 H 20.314672 v -3.837522 l 0.0971,-0.275116 h 11.209006 l 0.089,0.275116 z M 25.208235,17.875001 v -1.607989 h -3.215979 l 4.823966,-2.411981 v 1.607988 H 30.0322 Z M 2.5904451,17.061236 C 2.3615878,17.681074 2.1574473,18.309759 1.9785073,18.945805 H 10.602006 V 37.331696 H 41.150085 V 18.945805 h 8.871408 c -0.184075,-0.636272 -0.393416,-1.26496 -0.62753,-1.884569 H 38.720725 V 35.001194 H 12.908652 V 17.061236 Z'
|
||
};
|
||
|
||
const fireEvent = (node, type, detail, options) => {
|
||
options = options || {};
|
||
detail = detail === null || detail === undefined ? {} : detail;
|
||
const event = new Event(type, {
|
||
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
||
cancelable: Boolean(options.cancelable),
|
||
composed: options.composed === undefined ? true : options.composed
|
||
});
|
||
event.detail = detail;
|
||
node.dispatchEvent(event);
|
||
return event;
|
||
};
|
||
|
||
function hasConfigOrEntityChanged(element, changedProps) {
|
||
if (changedProps.has('_config')) {
|
||
return true;
|
||
}
|
||
|
||
const oldHass = changedProps.get('hass');
|
||
if (oldHass) {
|
||
return oldHass.states[element._config.entity] !== element.hass.states[element._config.entity];
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
class PVPCHourlyPricingCard extends LitElement {
|
||
static get properties() {
|
||
return {
|
||
_config: { type: Object },
|
||
hass: { type: Object }
|
||
};
|
||
}
|
||
|
||
static async getConfigElement() {
|
||
await import('./pvpc-hourly-pricing-card-editor.js');
|
||
return document.createElement('pvpc-hourly-pricing-card-editor');
|
||
}
|
||
|
||
static getStubConfig(hass, entities, entitiesFallback) {
|
||
const entity = Object.keys(hass.states).find((eid) =>
|
||
Object.keys(hass.states[eid].attributes).some((aid) => aid == 'min_price_at')
|
||
);
|
||
return { entity: entity };
|
||
}
|
||
|
||
setConfig(config) {
|
||
if (!config.entity) {
|
||
throw new Error('Please define a "Spain electricity hourly pricing (PVPC)" entity');
|
||
}
|
||
|
||
this._config = config;
|
||
|
||
this.setPVPCHourlyPricingObj();
|
||
}
|
||
|
||
setPVPCHourlyPricingObj() {
|
||
if (!this.hass) return;
|
||
|
||
this.pvpcHourlyPricingObj = this._config.entity in this.hass.states ? this.hass.states[this._config.entity] : null;
|
||
if (!this.pvpcHourlyPricingObj) return;
|
||
|
||
this.despiction = this.getDespiction(this.pvpcHourlyPricingObj.attributes);
|
||
}
|
||
|
||
shouldUpdate(changedProps) {
|
||
return hasConfigOrEntityChanged(this, changedProps);
|
||
}
|
||
|
||
updated(param) {
|
||
this.setPVPCHourlyPricingObj();
|
||
let chart = this.shadowRoot.getElementById('Chart');
|
||
if (chart) {
|
||
chart.data = this.ChartData;
|
||
chart.hass = this.hass;
|
||
}
|
||
}
|
||
|
||
render() {
|
||
if (!this._config || !this.hass) {
|
||
return html``;
|
||
}
|
||
|
||
this.setPVPCHourlyPricingObj();
|
||
this.numberElements = 0;
|
||
this.lang = this.hass.selectedLanguage || this.hass.language;
|
||
|
||
if (!this.pvpcHourlyPricingObj) {
|
||
return html`
|
||
<style>
|
||
.not-found {
|
||
flex: 1;
|
||
background-color: yellow;
|
||
padding: 8px;
|
||
}
|
||
</style>
|
||
<ha-card>
|
||
<div class="not-found">Entity not available: ${this._config.entity}</div>
|
||
</ha-card>
|
||
`;
|
||
}
|
||
|
||
return html`
|
||
<ha-card header="${this._config.name ? this._config.name : ''}">
|
||
${this._config.current !== false ? this.renderCurrent() : ''}
|
||
${this._config.details !== false ? this.renderDetails() : ''}
|
||
${this._config.graph !== false ? this.renderGraph() : ''}
|
||
${this._config.info !== false ? this.renderInfo() : ''}
|
||
</ha-card>
|
||
`;
|
||
}
|
||
|
||
renderCurrent() {
|
||
this.numberElements++;
|
||
const tariffPeriod = this.getTariffPeriod(this.pvpcHourlyPricingObj.attributes.tariff);
|
||
const style = getComputedStyle(document.body);
|
||
const iconColor = style.getPropertyValue(tariffPeriodIconColors[tariffPeriod]);
|
||
|
||
return html`
|
||
<div class="current tappable ${this.numberElements > 1 ? 'spacer' : ''}" @click="${this._handleClick}">
|
||
<svg class="period-icon" viewBox="0 0 52 52">
|
||
<circle fill="${iconColor}" r="25" cy="26" cx="26" />
|
||
<path fill="#f9f9f9" d="${tariffPeriodIcons[tariffPeriod]}" />
|
||
</svg>
|
||
|
||
<span class="currentPrice">${this.getFixedFloat(this.pvpcHourlyPricingObj.state)}</span>
|
||
<span class="currentPriceUnit"> ${this.pvpcHourlyPricingObj.attributes.unit_of_measurement}</span>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
renderDetails() {
|
||
if (!this.despiction) {
|
||
return html``;
|
||
}
|
||
|
||
const minPrice = this.getFixedFloat(this.despiction.minPrice);
|
||
const minPriceFrom = this.getTimeString(new Date().setHours(this.despiction.minIndex, 0));
|
||
const minPriceTo = this.getTimeString(new Date().setHours(this.despiction.minIndex + 1, 0));
|
||
const maxPrice = this.getFixedFloat(this.despiction.maxPrice);
|
||
const maxPriceFrom = this.getTimeString(new Date().setHours(this.despiction.maxIndex, 0));
|
||
const maxPriceTo = this.getTimeString(new Date().setHours(this.despiction.maxIndex + 1, 0));
|
||
const minPriceNextDay = this.getFixedFloat(this.despiction.minPriceNextDay);
|
||
const minPriceFromNextDay = this.getTimeString(new Date().setHours(this.despiction.minIndexNextDay, 0));
|
||
const minPriceToNextDay = this.getTimeString(new Date().setHours(this.despiction.minIndexNextDay + 1, 0));
|
||
const maxPriceNextDay = this.getFixedFloat(this.despiction.maxPriceNextDay);
|
||
const maxPriceFromNextDay = this.getTimeString(new Date().setHours(this.despiction.maxIndexNextDay, 0));
|
||
const maxPriceToNextDay = this.getTimeString(new Date().setHours(this.despiction.maxIndexNextDay + 1, 0));
|
||
|
||
this.numberElements++;
|
||
|
||
return html`
|
||
<ul class="details tappable ${this.numberElements > 1 ? 'spacer' : ''}" @click="${this._handleClick}">
|
||
<li>
|
||
<ha-icon icon="mdi:thumb-up-outline"></ha-icon>
|
||
${this.ll('minPrice')} ${minPrice}${this.pvpcHourlyPricingObj.attributes.unit_of_measurement}
|
||
${this.ll('from')} ${minPriceFrom} ${this.ll('to')} ${minPriceTo}
|
||
</li>
|
||
<li>
|
||
<ha-icon icon="mdi:thumb-down-outline"></ha-icon>
|
||
${this.ll('maxPrice')} ${maxPrice}${this.pvpcHourlyPricingObj.attributes.unit_of_measurement}
|
||
${this.ll('from')} ${maxPriceFrom} ${this.ll('to')} ${maxPriceTo}
|
||
</li>
|
||
${this.despiction.minPriceNextDay
|
||
? html` <li>
|
||
<ha-icon icon="mdi:thumb-up-outline"></ha-icon>
|
||
${this.ll('minPriceNextDay')}
|
||
${minPriceNextDay}${this.pvpcHourlyPricingObj.attributes.unit_of_measurement} ${this.ll('from')}
|
||
${minPriceFromNextDay} ${this.ll('to')} ${minPriceToNextDay}
|
||
</li>
|
||
<li>
|
||
<ha-icon icon="mdi:thumb-down-outline"></ha-icon>
|
||
${this.ll('maxPriceNextDay')}
|
||
${maxPriceNextDay}${this.pvpcHourlyPricingObj.attributes.unit_of_measurement} ${this.ll('from')}
|
||
${maxPriceFromNextDay} ${this.ll('to')} ${maxPriceToNextDay}
|
||
</li>`
|
||
: ''}
|
||
</ul>
|
||
`;
|
||
}
|
||
|
||
renderGraph() {
|
||
if (!this.despiction) {
|
||
return html``;
|
||
}
|
||
|
||
this.numberElements++;
|
||
|
||
this.drawChart();
|
||
|
||
return html`
|
||
<div class="clear ${this.numberElements > 1 ? 'spacer' : ''}">
|
||
<ha-chart-base id="Chart"></ha-chart-base>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
renderInfo() {
|
||
if (!this.despiction) {
|
||
return html``;
|
||
}
|
||
|
||
this.numberElements++;
|
||
|
||
if (!this.despiction.minPriceNextDay) {
|
||
return html`
|
||
<div class="info clear ${this.numberElements > 1 ? 'spacer' : ''}">${this.ll('infoNoNextDay')}</div>
|
||
`;
|
||
} else {
|
||
return html``;
|
||
}
|
||
}
|
||
|
||
drawChart() {
|
||
if (!this.despiction) return;
|
||
|
||
const that = this;
|
||
|
||
const style = getComputedStyle(document.body);
|
||
const legendTextColor = style.getPropertyValue('--primary-text-color');
|
||
const axisTextColor = style.getPropertyValue('--secondary-text-color');
|
||
const dividerColor = style.getPropertyValue('--divider-color');
|
||
const selectionColor = style.getPropertyValue('--paper-grey-500');
|
||
const today = new Date();
|
||
const minIndex = this.despiction.minIndex;
|
||
const maxIndex = this.despiction.maxIndex;
|
||
const minIndexNextDay = this.despiction.minIndexNextDay;
|
||
const maxIndexNextDay = this.despiction.maxIndexNextDay;
|
||
const hasNextDayData = this.despiction.pricesNextDay[0] !== undefined;
|
||
const minIcon = '▼';
|
||
const maxIcon = '▲';
|
||
|
||
const chartOptions = {
|
||
type: 'line',
|
||
data: {
|
||
labels: this.despiction.dateTime,
|
||
datasets: [
|
||
{
|
||
label: that.getDateString(today),
|
||
type: 'line',
|
||
data: this.despiction.prices,
|
||
borderWidth: 2.0,
|
||
pointRadius: 0.0,
|
||
pointHitRadius: 0.0,
|
||
fill: false,
|
||
steppedLine: true
|
||
}
|
||
]
|
||
},
|
||
options: {
|
||
animation: {
|
||
duration: 300,
|
||
easing: 'linear',
|
||
onComplete: function () {
|
||
const chartInstance = this.chart;
|
||
const ctx = chartInstance.ctx;
|
||
const fontSize = 12;
|
||
const fontStyle = 'normal';
|
||
const fontFamily = 'Roboto';
|
||
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
|
||
const meta = chartInstance.controller.getDatasetMeta(0);
|
||
const minBarStart = meta.data[minIndex];
|
||
const minBarEnd = meta.data[minIndex + 1];
|
||
const pointToPointCenterXOffset = (minBarEnd._model.x - minBarStart._model.x) / 2;
|
||
const maxBar = meta.data[maxIndex];
|
||
const iconYOffset = 8;
|
||
ctx.fillStyle = meta.dataset._model.borderColor;
|
||
ctx.fillText(minIcon, minBarStart._model.x + pointToPointCenterXOffset, minBarStart._model.y - iconYOffset);
|
||
ctx.fillText(maxIcon, maxBar._model.x + pointToPointCenterXOffset, maxBar._model.y - iconYOffset);
|
||
|
||
ctx.save();
|
||
const selectedIndex =
|
||
chartInstance.tooltip._active &&
|
||
chartInstance.tooltip._active.length > 0 &&
|
||
chartInstance.tooltip._active[0]._index < 24
|
||
? chartInstance.tooltip._active[0]._index
|
||
: today.getHours();
|
||
const yaxis = meta.controller.chart.scales['y-axis-0'];
|
||
const xBarStart = meta.data[selectedIndex]._model.x;
|
||
const xBarEnd = meta.data[selectedIndex + 1]._model.x;
|
||
const yBarStart = yaxis.top;
|
||
const yBarEnd = yaxis.bottom;
|
||
ctx.globalAlpha = 0.6;
|
||
ctx.beginPath();
|
||
ctx.moveTo(xBarStart, yBarStart);
|
||
ctx.lineTo(xBarStart, yBarEnd);
|
||
ctx.strokeStyle = selectionColor;
|
||
ctx.stroke();
|
||
ctx.beginPath();
|
||
ctx.moveTo(xBarEnd, yBarStart);
|
||
ctx.lineTo(xBarEnd, yBarEnd);
|
||
ctx.strokeStyle = selectionColor;
|
||
ctx.stroke();
|
||
ctx.globalAlpha = 0.3;
|
||
ctx.fillStyle = selectionColor;
|
||
ctx.fillRect(xBarStart, yBarStart, xBarEnd - xBarStart, yBarEnd - yBarStart);
|
||
ctx.restore();
|
||
|
||
if (hasNextDayData) {
|
||
const meta_next_day = chartInstance.controller.getDatasetMeta(1);
|
||
const minNextDayBar = meta_next_day.data[minIndexNextDay];
|
||
const maxNextDayBar = meta_next_day.data[maxIndexNextDay];
|
||
ctx.fillStyle = meta_next_day.dataset._model.borderColor;
|
||
ctx.fillText(
|
||
minIcon,
|
||
minNextDayBar._model.x + pointToPointCenterXOffset,
|
||
minNextDayBar._model.y - iconYOffset
|
||
);
|
||
ctx.fillText(
|
||
maxIcon,
|
||
maxNextDayBar._model.x + pointToPointCenterXOffset,
|
||
maxNextDayBar._model.y - iconYOffset
|
||
);
|
||
}
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
labels: {
|
||
fontColor: legendTextColor,
|
||
fontSize: 14,
|
||
usePointStyle: true,
|
||
boxWidth: 6
|
||
}
|
||
},
|
||
scales: {
|
||
xAxes: [
|
||
{
|
||
type: 'time',
|
||
maxBarThickness: 15,
|
||
display: false,
|
||
ticks: {
|
||
display: false
|
||
},
|
||
gridLines: {
|
||
display: false
|
||
}
|
||
},
|
||
{
|
||
position: 'bottom',
|
||
gridLines: {
|
||
display: true,
|
||
drawTicks: false,
|
||
drawBorder: false,
|
||
color: dividerColor
|
||
},
|
||
ticks: {
|
||
display: true,
|
||
padding: 10,
|
||
source: 'labels',
|
||
autoSkip: true,
|
||
fontColor: axisTextColor,
|
||
maxRotation: 0,
|
||
callback: function (value, index, values) {
|
||
return that.getHourString.call(that, value);
|
||
}
|
||
}
|
||
}
|
||
],
|
||
yAxes: [
|
||
{
|
||
position: 'left',
|
||
gridLines: {
|
||
display: true,
|
||
drawBorder: false,
|
||
drawTicks: false,
|
||
color: dividerColor,
|
||
borderDash: [4, 6]
|
||
},
|
||
ticks: {
|
||
display: true,
|
||
padding: 10,
|
||
fontColor: axisTextColor
|
||
}
|
||
}
|
||
]
|
||
},
|
||
tooltips: {
|
||
mode: 'index',
|
||
intersect: false,
|
||
callbacks: {
|
||
title: function (items, data) {
|
||
const index = items[0].index != 24 ? items[0].index : (items[0].index = 23);
|
||
|
||
const date = new Date(data.labels[index]);
|
||
const initDate = that.getTimeString(date);
|
||
const endDate = that.getTimeString(date.setHours(date.getHours() + 1));
|
||
return initDate + ' - ' + endDate;
|
||
},
|
||
label: function (tooltipItems, data) {
|
||
let icon;
|
||
const index = tooltipItems.index != 24 ? tooltipItems.index : (tooltipItems.index = 23);
|
||
|
||
if (tooltipItems.datasetIndex === 0) {
|
||
if (index == minIndex) {
|
||
icon = minIcon;
|
||
} else if (index == maxIndex) {
|
||
icon = maxIcon;
|
||
}
|
||
} else if (tooltipItems.datasetIndex === 1) {
|
||
if (index == minIndexNextDay) {
|
||
icon = minIcon;
|
||
} else if (index == maxIndexNextDay) {
|
||
icon = maxIcon;
|
||
}
|
||
}
|
||
|
||
const labelTitle = data.datasets[tooltipItems.datasetIndex].label || '';
|
||
const label =
|
||
labelTitle +
|
||
': ' +
|
||
parseFloat(tooltipItems.value).toFixed(5) +
|
||
' ' +
|
||
that.pvpcHourlyPricingObj.attributes.unit_of_measurement +
|
||
' ';
|
||
|
||
return icon ? label + icon : label;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
if (hasNextDayData) {
|
||
chartOptions.data.datasets.push({
|
||
label: that.getDateString(today.setDate(today.getDate() + 1)),
|
||
type: 'line',
|
||
data: this.despiction.pricesNextDay,
|
||
borderWidth: 2.0,
|
||
pointRadius: 0.0,
|
||
pointHitRadius: 0.0,
|
||
fill: false,
|
||
steppedLine: true
|
||
});
|
||
}
|
||
|
||
this.ChartData = chartOptions;
|
||
}
|
||
|
||
getDespiction(attributes) {
|
||
const priceRegex = /price_\d\dh/;
|
||
const priceNextDayRegex = /price_(next|last)_day_\d\dh/;
|
||
|
||
const priceArray = Object.keys(attributes)
|
||
.filter((key) => priceRegex.test(key))
|
||
.map((key) => attributes[key]);
|
||
const priceNextDayArray = Object.keys(attributes)
|
||
.filter((key) => priceNextDayRegex.test(key))
|
||
.map((key) => attributes[key]);
|
||
|
||
let data = [];
|
||
let dateTime = [];
|
||
let prices = [];
|
||
let pricesNextDay = [];
|
||
|
||
for (let index = 0; index < 24; index++) {
|
||
dateTime.push(new Date().setHours(index, 0));
|
||
prices.push(priceArray[index]);
|
||
pricesNextDay.push(priceNextDayArray[index]);
|
||
}
|
||
|
||
dateTime.push(new Date().setHours(24, 0));
|
||
prices.push(priceArray[23]);
|
||
pricesNextDay.push(priceNextDayArray[23]);
|
||
|
||
data.dateTime = dateTime;
|
||
data.prices = prices;
|
||
data.pricesNextDay = pricesNextDay;
|
||
|
||
data.minPrice = Math.min.apply(null, prices);
|
||
data.maxPrice = Math.max.apply(null, prices);
|
||
data.minIndex = prices.indexOf(data.minPrice);
|
||
data.maxIndex = prices.indexOf(data.maxPrice);
|
||
data.minPriceNextDay = Math.min.apply(null, pricesNextDay);
|
||
data.maxPriceNextDay = Math.max.apply(null, pricesNextDay);
|
||
data.minIndexNextDay = pricesNextDay.indexOf(data.minPriceNextDay);
|
||
data.maxIndexNextDay = pricesNextDay.indexOf(data.maxPriceNextDay);
|
||
|
||
return data;
|
||
}
|
||
|
||
getTariffPeriod(tariff) {
|
||
let period;
|
||
|
||
switch (tariff) {
|
||
case 'normal':
|
||
period = 'normal';
|
||
break;
|
||
case 'discrimination':
|
||
const utcHours = new Date().getUTCHours();
|
||
if (utcHours >= 21 || utcHours < 11) {
|
||
period = 'valley';
|
||
} else {
|
||
period = 'peak';
|
||
}
|
||
break;
|
||
case 'electric_car':
|
||
const hours = new Date().getHours();
|
||
if (hours >= 1 && hours < 7) {
|
||
period = 'super-valley';
|
||
} else if (hours >= 13 && hours < 23) {
|
||
period = 'peak';
|
||
} else {
|
||
period = 'valley';
|
||
}
|
||
break;
|
||
default:
|
||
period = 'error';
|
||
}
|
||
|
||
return period;
|
||
}
|
||
|
||
getDateString(datetime) {
|
||
return new Date(datetime).toLocaleDateString(this.lang, {
|
||
day: '2-digit',
|
||
month: '2-digit',
|
||
year: 'numeric'
|
||
});
|
||
}
|
||
|
||
getHourString(datetime) {
|
||
return new Date(datetime).toLocaleTimeString(this.lang, { hour: '2-digit', hour12: false });
|
||
}
|
||
|
||
getTimeString(datetime) {
|
||
return new Date(datetime).toLocaleTimeString(this.lang, { hour: '2-digit', minute: '2-digit', hour12: false });
|
||
}
|
||
|
||
getFixedFloat(number) {
|
||
return parseFloat(number).toFixed(5);
|
||
}
|
||
|
||
_handleClick() {
|
||
fireEvent(this, 'hass-more-info', { entityId: this._config.entity });
|
||
}
|
||
|
||
getCardSize() {
|
||
return this.numberElements || 3;
|
||
}
|
||
|
||
static get styles() {
|
||
return css`
|
||
ha-card {
|
||
margin: auto;
|
||
padding-top: 1.3em;
|
||
padding-bottom: 1.3em;
|
||
padding-left: 1em;
|
||
padding-right: 1em;
|
||
position: relative;
|
||
}
|
||
|
||
ha-icon {
|
||
color: var(--paper-item-icon-color);
|
||
}
|
||
|
||
.spacer {
|
||
padding-top: 1em;
|
||
}
|
||
|
||
.clear {
|
||
clear: both;
|
||
}
|
||
|
||
.tappable {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.current {
|
||
height: 5.5em;
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.period-icon {
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
width: 5.5em;
|
||
height: 5.5em;
|
||
}
|
||
|
||
.currentPrice {
|
||
font-weight: 300;
|
||
font-size: 4em;
|
||
color: var(--primary-text-color);
|
||
margin-top: 0.5em;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.currentPriceUnit {
|
||
font-weight: 300;
|
||
font-size: 1.5em;
|
||
vertical-align: super;
|
||
color: var(--primary-text-color);
|
||
right: 0em;
|
||
top: 0em;
|
||
position: absolute;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.details {
|
||
font-weight: 300;
|
||
color: var(--primary-text-color);
|
||
list-style: none;
|
||
padding-right: 1em;
|
||
padding-left: 1em;
|
||
}
|
||
|
||
.details li {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.details ha-icon {
|
||
height: 22px;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.info {
|
||
color: var(--primary-text-color);
|
||
text-align: center;
|
||
padding-right: 1em;
|
||
padding-left: 1em;
|
||
}
|
||
`;
|
||
}
|
||
|
||
ll(str) {
|
||
if (locale[this.lang] === undefined) return locale.en[str];
|
||
return locale[this.lang][str];
|
||
}
|
||
}
|
||
|
||
customElements.define('pvpc-hourly-pricing-card', PVPCHourlyPricingCard);
|