diff --git a/package.json b/package.json index 126e4de..6471f95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "givtcp-battery-card", - "version": "0.1.9", + "version": "0.2.0", "description": "Lovelace card to display GivTCP battery info", "private": true, "type": "module", diff --git a/src/config-utils.ts b/src/config-utils.ts index cfe6bde..b8ec25a 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -17,6 +17,9 @@ import { ICON_STATUS_CHARGING, ICON_STATUS_DISCHARGING, DISPLAY_BATTERY_RATES, + USE_CUSTOM_DOD, + CUSTOM_DOD, + CALCULATE_RESERVE_FROM_DOD, DISPLAY_CUSTOM_DOD_STATS, } from "./constants"; export class ConfigUtils { @@ -40,6 +43,10 @@ export class ConfigUtils { icon_status_charging: ICON_STATUS_CHARGING, icon_status_discharging: ICON_STATUS_DISCHARGING, display_battery_rates: DISPLAY_BATTERY_RATES, + use_custom_dod: USE_CUSTOM_DOD, + custom_dod: CUSTOM_DOD, + calculate_reserve_from_dod: CALCULATE_RESERVE_FROM_DOD, + display_custom_dod_stats: DISPLAY_CUSTOM_DOD_STATS, }; } diff --git a/src/constants.ts b/src/constants.ts index a7f81e1..d7acd1a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,3 +24,18 @@ export const ICON_STATUS_CHARGING = 'mdi:lightning-bolt'; export const ICON_STATUS_DISCHARGING = 'mdi:home-battery'; export const DISPLAY_BATTERY_RATES = true; + +export const USE_CUSTOM_DOD = false; + +export const CUSTOM_DOD = 100.0; + +export const CALCULATE_RESERVE_FROM_DOD = false; + +export const DISPLAY_CUSTOM_DOD_STATS = true; + +export const DISPLAY_UNITS = { + W: "W", + KW: "kW", + WH: "Wh", + KWH: "kWh", +} diff --git a/src/editor.ts b/src/editor.ts index bcaddb7..0d92400 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -46,7 +46,8 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE selector: { number: { min: 0, - max: 100 + max: 100, + unit_of_measurement: "%", } } }, @@ -65,7 +66,8 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE selector: { number: { min: 0, - max: 100 + max: 100, + unit_of_measurement: "%", } } }, @@ -84,7 +86,8 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE selector: { number: { min: 0, - max: 100 + max: 100, + unit_of_measurement: "%", } } }, @@ -103,7 +106,8 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE selector: { number: { min: 0, - max: 100 + max: 100, + unit_of_measurement: "%", } } }, @@ -133,7 +137,7 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE }, { name: 'display_type', - label: 'Display type (0: Wh | 1: kWh | 2: Dynamic)', + label: 'Display type (0: Wh/W | 1: kWh/kW | 2: Dynamic)', default: defaults.display_type, selector: { number: { @@ -185,6 +189,43 @@ export class GivTCPBatteryCardEditor extends LitElement implements LovelaceCardE boolean: {} } }, + { + name: 'use_custom_dod', + label: 'EXPERIMENTAL! Use custom DoD to override GivTCP battery capacity value.', + default: defaults.use_custom_dod, + selector: { + boolean: {} + } + }, + { + name: 'display_custom_dod_stats', + label: 'EXPERIMENTAL! Display the custom DOD stats', + default: defaults.display_custom_dod_stats, + selector: { + boolean: {} + } + }, + { + name: 'custom_dod', + label: 'EXPERIMENTAL! Custom DoD as percentage to override GivTCP battery capacity value.', + default: defaults.custom_dod, + selector: { + number: { + min: 0, + max: 100, + step: "any", + unit_of_measurement: "%", + } + } + }, + { + name: 'calculate_reserve_from_dod', + label: 'EXPERIMENTAL! Use custom DoD to calculate the battery reserve value', + default: defaults.calculate_reserve_from_dod, + selector: { + boolean: {} + } + }, ]; } diff --git a/src/givtcp-battery-card.ts b/src/givtcp-battery-card.ts index fe30524..b4adb63 100644 --- a/src/givtcp-battery-card.ts +++ b/src/givtcp-battery-card.ts @@ -13,7 +13,10 @@ import { DISPLAY_ABS_POWER, DISPLAY_DP, DISPLAY_TYPE, - DISPLAY_TYPE_OPTIONS, ICON_STATUS_CHARGING, ICON_STATUS_DISCHARGING, ICON_STATUS_IDLE, + DISPLAY_TYPE_OPTIONS, + ICON_STATUS_CHARGING, + ICON_STATUS_DISCHARGING, + ICON_STATUS_IDLE, SOC_THRESH_HIGH, SOC_THRESH_HIGH_COLOUR, SOC_THRESH_LOW, @@ -24,6 +27,11 @@ import { SOC_THRESH_V_HIGH_COLOUR, SOC_THRESH_V_LOW_COLOUR, DISPLAY_BATTERY_RATES, + USE_CUSTOM_DOD, + CUSTOM_DOD, + CALCULATE_RESERVE_FROM_DOD, + DISPLAY_CUSTOM_DOD_STATS, + DISPLAY_UNITS, } from "./constants"; import './components/countdown' @@ -32,7 +40,7 @@ import { styleCss } from './style'; import { version } from '../package.json'; import {ConfigUtils} from "./config-utils"; -import {GivTcpBatteryStats} from "./types"; +import {GivTcpBatteryStats, GivTcpStats} from "./types"; // This puts your card into the UI card picker dialog (window as any).customCards = (window as any).customCards || []; @@ -159,309 +167,225 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { return this.clientHeight > 0 ? Math.ceil(this.clientHeight / 50) : 3; } - setRawValues(): GivTcpBatteryStats { - const rawSocPercentEntity = this._getSocEntity - const rawBatteryPowerEntity = this._getBatteryPowerEntity - const rawSocEnergyEntity = this._getSocKwhEntity - const rawDischargePowerEntity = this._getDischargePowerEntity - const rawChargePowerEntity = this._getChargePowerEntity - const rawBatteryCapacityEntity = this._getBatteryCapacityKwhEntity - const rawBatteryPowerReserveEntity = this._getBatteryPowerReserve - const rawBatteryChargeRate = this._getBatteryChargeRate - const rawBatteryDischargeRate = this._getBatteryDischargeRate - - const socPercent = { - rawState: rawSocPercentEntity.state, - uom: rawSocPercentEntity.attributes?.unit_of_measurement, - value: 0, - display: 0, - displayStr: '', - } - - const batteryPower = { - rawState: rawBatteryPowerEntity.state, - uom: rawBatteryPowerEntity.attributes?.unit_of_measurement, - w: 0, - kW: 0.0, - display: 0, - displayStr: '', - displayUnit: '', - } - - const socEnergy = { - rawState: rawSocEnergyEntity.state, - uom: rawSocEnergyEntity.attributes?.unit_of_measurement, - Wh: 0, - kWh: 0.0, - display: 0, - displayStr: '', - } - - const dischargePower = { - rawState: rawDischargePowerEntity.state, - uom: rawDischargePowerEntity.attributes?.unit_of_measurement, - w: 0, - kW: 0.0, - display: 0, - displayStr: '', - } - - const chargePower = { - rawState: rawChargePowerEntity.state, - uom: rawChargePowerEntity.attributes?.unit_of_measurement, - w: 0, - kW: 0.0, - display: 0, - displayStr: '', - } - - const batteryCapacity = { - rawState: rawBatteryCapacityEntity.state, - uom: rawBatteryCapacityEntity.attributes?.unit_of_measurement, - Wh: 0, - kWh: 0.0, - display: 0, - displayStr: '', - } - - const batteryPowerReservePercent = { - rawState: rawBatteryPowerReserveEntity.state, - uom: rawBatteryPowerReserveEntity.attributes?.unit_of_measurement, - value: 0, - display: 0, - displayStr: '', - } - - const batteryPowerReserveEnergy = { - Wh: 0, - kWh: 0.0, - display: 0, - displayStr: '', - } - - const chargeRate = { - rawState: rawBatteryChargeRate.state, - uom: rawBatteryChargeRate.attributes?.unit_of_measurement, - w: 0, - kW: 0.0, - display: 0, - displayStr: '', - } - - const dischargeRate = { - rawState: rawBatteryDischargeRate.state, - uom: rawBatteryDischargeRate.attributes?.unit_of_measurement, - w: 0, - kW: 0.0, - display: 0, - displayStr: '', + getDp(): number { + let dp = parseInt((this.config.display_dp !== undefined) ? this.config.display_dp : DISPLAY_DP, 10); + if(dp > 3) { + dp = 3; + } + + if(dp < 1) { + dp = 1; } + return dp; + } + getPercentageStats(entity: HassEntity): GivTcpStats { + const s = entity.state + const uom = entity.attributes?.unit_of_measurement; return { - socPercent, - batteryPower, - socEnergy, - dischargePower, - chargePower, - batteryCapacity, - batteryPowerReservePercent, - batteryPowerReserveEnergy, - chargeRate, - dischargeRate, + rawState: s, + uom: uom, + value: parseInt(s, 10), + kValue: 0, + display: parseInt(s, 10), + displayStr: `${parseInt(s, 10)}%`, + displayUnit: "%", } } - calculateStats(): void { + getStandardisedUom(uom: string | undefined): string { + + if(!uom) { + return ""; + } + + switch(uom.toLowerCase()) { + case "w": + return DISPLAY_UNITS.W; + case "wh": + return DISPLAY_UNITS.WH; + case "kw": + return DISPLAY_UNITS.KW; + case "kwh": + return DISPLAY_UNITS.KWH; + default: + return uom; + } + } - const states = this.setRawValues(); + isWorWh(uom: string): boolean { + return (uom === DISPLAY_UNITS.W || uom === DISPLAY_UNITS.WH); + } + + getGivTcpStats(state: string, uomRaw: string | undefined): GivTcpStats { const displayType = (this.config.display_type !== undefined) ? this.config.display_type : DISPLAY_TYPE; const displayAbsPower = (this.config.display_abs_power !== undefined) ? this.config.display_abs_power : DISPLAY_ABS_POWER; - let dp = parseInt((this.config.display_dp !== undefined) ? this.config.display_dp : DISPLAY_DP, 10); + const dp = this.getDp(); - if(dp > 3) { - dp = 3; - } + const uom = this.getStandardisedUom(uomRaw); - if(dp < 1) { - dp = 1; - } + const rawAsNum = this.isWorWh(uom) ? parseInt(state, 10) : parseFloat(state); + const value = this.isWorWh(uom) ? rawAsNum : rawAsNum * 1000; + const kValue = this.isWorWh(uom) ? this.convertToKillo(rawAsNum, 3) : rawAsNum; + const displayK = this.convertToKillo(value, dp); + + const displayUom = this.isWorWh(uom) ? uom : this.getStandardisedUom(`k${uom}`); + const displayKUom = this.isWorWh(uom) ? this.getStandardisedUom(`k${uom}`) : uom; + + let display = 0; + let displayStr = ""; + let displayUnit = ""; - states.socPercent.value = parseInt(states.socPercent.rawState, 10); - states.socPercent.display = parseInt(states.socPercent.rawState, 10); - states.socPercent.displayStr = `${parseInt(states.socPercent.rawState, 10)}%`; - - const batteryPowerReservePercent = parseInt(states.batteryPowerReservePercent.rawState, 10); - states.batteryPowerReservePercent.value = batteryPowerReservePercent; - states.batteryPowerReservePercent.display = batteryPowerReservePercent; - states.batteryPowerReservePercent.displayStr = `${batteryPowerReservePercent}%`; - - const batteryCapacityKwh = parseFloat(states.batteryCapacity.rawState); - const batteryCapacityWh = batteryCapacityKwh * 1000; - states.batteryCapacity.kWh = batteryCapacityKwh; - states.batteryCapacity.Wh = batteryCapacityWh; - - const batteryPowerReserveEnergyWh = Math.round(batteryCapacityWh * (batteryPowerReservePercent / 100)) - const batteryPowerReserveEnergyKWh = this.convertToKillo(batteryPowerReserveEnergyWh, 3); - states.batteryPowerReserveEnergy.Wh = batteryPowerReserveEnergyWh; - states.batteryPowerReserveEnergy.kWh = batteryPowerReserveEnergyKWh; - - const batteryPowerRaw = (states.batteryPower.uom === "W") ? parseInt(states.batteryPower.rawState, 10) : parseFloat(states.batteryPower.rawState); - const batteryPowerW = (states.batteryPower.uom === "W") ? batteryPowerRaw : batteryPowerRaw * 1000; - const batteryPowerKW = (states.batteryPower.uom === "W") ? this.convertToKillo(batteryPowerRaw, 3) : batteryPowerRaw; - states.batteryPower.w = batteryPowerW; - states.batteryPower.kW = batteryPowerKW; - - const chargePowerRaw = (states.chargePower.uom === "W") ? parseInt(states.chargePower.rawState, 10) : parseFloat(states.chargePower.rawState); - const chargePowerW = (states.chargePower.uom === "W") ? chargePowerRaw : chargePowerRaw * 1000; - const chargePowerKW = (states.chargePower.uom === "W") ? this.convertToKillo(chargePowerRaw, 3) : chargePowerRaw; - states.chargePower.w = chargePowerW; - states.chargePower.kW = chargePowerKW; - - const dischargePowerRaw = (states.dischargePower.uom === "W") ? parseInt(states.dischargePower.rawState, 10) : parseFloat(states.dischargePower.rawState); - const dischargePowerW = (states.dischargePower.uom === "W") ? dischargePowerRaw : dischargePowerRaw * 1000; - const dischargePowerKW = (states.dischargePower.uom === "W") ? this.convertToKillo(dischargePowerRaw, 3) : dischargePowerRaw; - states.dischargePower.w = dischargePowerW; - states.dischargePower.kW = dischargePowerKW; - - const socEnergyRaw = (states.socEnergy.uom === "Wh") ? parseInt(states.socEnergy.rawState, 10) : parseFloat(states.socEnergy.rawState); - const socEnergyWh = (states.socEnergy.uom === "Wh") ? socEnergyRaw : socEnergyRaw * 1000; - const socEnergyKWh = (states.socEnergy.uom === "Wh") ? this.convertToKillo(socEnergyRaw, 3) : socEnergyRaw; - states.socEnergy.Wh = socEnergyWh; - states.socEnergy.kWh = socEnergyKWh; - - const chargeRateRaw = (states.chargeRate.uom === "W") ? parseInt(states.chargeRate.rawState, 10) : parseFloat(states.chargeRate.rawState); - const chargeRateW = (states.chargeRate.uom === "W") ? chargeRateRaw : chargeRateRaw * 1000; - const chargeRateKW = (states.chargeRate.uom === "W") ? this.convertToKillo(chargeRateRaw, 3) : chargeRateRaw; - states.chargeRate.w = chargeRateW; - states.chargeRate.kW = chargeRateKW; - - const dischargeRateRaw = (states.dischargeRate.uom === "W") ? parseInt(states.dischargeRate.rawState, 10) : parseFloat(states.dischargeRate.rawState); - const dischargeRateW = (states.dischargeRate.uom === "W") ? dischargeRateRaw : dischargeRateRaw * 1000; - const dischargeRateKW = (states.dischargeRate.uom === "W") ? this.convertToKillo(dischargeRateRaw, 3) : dischargeRateRaw; - states.dischargeRate.w = dischargeRateW; - states.dischargeRate.kW = dischargeRateKW; - - // format displays switch(displayType) { case DISPLAY_TYPE_OPTIONS.WH: default: - states.batteryCapacity.display = batteryCapacityWh; - states.batteryCapacity.displayStr = `${batteryCapacityWh} Wh`; - states.batteryPower.display = (displayAbsPower) ? Math.abs(batteryPowerW) : batteryPowerW; - states.batteryPower.displayStr = `${(displayAbsPower) ? Math.abs(batteryPowerW) : batteryPowerW} W`; - states.chargePower.display = chargePowerW; - states.chargePower.displayStr = `${chargePowerW} W`; - states.dischargePower.display = dischargePowerW; - states.dischargePower.displayStr = `${dischargePowerW} W`; - states.socEnergy.display = socEnergyWh; - states.socEnergy.displayStr = `${socEnergyWh} Wh`; - states.batteryPowerReserveEnergy.display = batteryPowerReserveEnergyWh; - states.batteryPowerReserveEnergy.displayStr = `${batteryPowerReserveEnergyWh} Wh`; - states.batteryPower.displayUnit = 'W'; - states.chargeRate.display = chargeRateW; - states.chargeRate.displayStr = `${chargeRateW} W`; - states.dischargeRate.display = dischargeRateW; - states.dischargeRate.displayStr = `${dischargeRateW} W`; + display = (displayAbsPower) ? Math.abs(value) : value; + displayStr = `${(displayAbsPower) ? Math.abs(value) : value} ${displayUom}`; + displayUnit = displayUom; break; case DISPLAY_TYPE_OPTIONS.KWH: - states.batteryCapacity.display = batteryCapacityKwh; - states.batteryCapacity.displayStr = `${batteryCapacityKwh} kWh`; - states.batteryPower.display = (displayAbsPower) ? this.convertToKillo(Math.abs(batteryPowerW), dp) : this.convertToKillo(batteryPowerW, dp); - states.batteryPower.displayStr = `${(displayAbsPower) ? this.convertToKillo(Math.abs(batteryPowerW), dp) : this.convertToKillo(batteryPowerW, dp)} kW`; - states.chargePower.display = this.convertToKillo(chargePowerW, dp); - states.chargePower.displayStr = `${this.convertToKillo(chargePowerW, dp)} kW`; - states.dischargePower.display = this.convertToKillo(dischargePowerW, dp); - states.dischargePower.displayStr = `${this.convertToKillo(dischargePowerW, dp)} kW`; - states.socEnergy.display = socEnergyKWh; - states.socEnergy.displayStr = `${socEnergyKWh} kWh`; - states.batteryPowerReserveEnergy.display = this.convertToKillo(batteryPowerReserveEnergyWh, dp); - states.batteryPowerReserveEnergy.displayStr = `${this.convertToKillo(batteryPowerReserveEnergyWh, dp)} kWh`; - states.batteryPower.displayUnit = 'kW'; - states.chargeRate.display = this.convertToKillo(chargeRateW, dp); - states.chargeRate.displayStr = `${this.convertToKillo(chargeRateW, dp)} kW`; - states.dischargeRate.display = this.convertToKillo(dischargeRateW, dp); - states.dischargeRate.displayStr = `${this.convertToKillo(dischargeRateW, dp)} kW`; + display = (displayAbsPower) ? Math.abs(displayK) : displayK; + displayStr = `${(displayAbsPower) ? Math.abs(displayK) : displayK} ${displayKUom}`; + displayUnit = displayKUom; break; case DISPLAY_TYPE_OPTIONS.DYNAMIC: - states.batteryCapacity.display = (Math.abs(batteryCapacityWh) >= 1000) ? batteryCapacityKwh : batteryCapacityWh; - states.batteryCapacity.displayStr = (Math.abs(batteryCapacityWh) >= 1000) ? `${batteryCapacityKwh} kWh` : `${batteryCapacityWh} Wh`; - - states.batteryPower.display = (Math.abs(batteryPowerW) >= 1000) ? ((displayAbsPower) ? this.convertToKillo(Math.abs(batteryPowerW), dp) : this.convertToKillo(batteryPowerW, dp)) : ((displayAbsPower) ? Math.abs(batteryPowerW) : batteryPowerW); - states.batteryPower.displayStr = (Math.abs(batteryPowerW) >= 1000) ? `${(displayAbsPower) ? this.convertToKillo(Math.abs(batteryPowerW), dp) : this.convertToKillo(batteryPowerW, dp)} kW` : `${(displayAbsPower) ? Math.abs(batteryPowerW) : batteryPowerW} W`; - states.chargePower.display = (Math.abs(chargePowerW) >= 1000) ? this.convertToKillo(chargePowerW, dp) : chargePowerW; - states.chargePower.displayStr = (Math.abs(chargePowerW) >= 1000) ? `${this.convertToKillo(chargePowerW, dp)} kW` : `${chargePowerW} W`; - states.dischargePower.display = (Math.abs(dischargePowerW) >= 1000) ? this.convertToKillo(dischargePowerW, dp) : dischargePowerW; - states.dischargePower.displayStr = (Math.abs(dischargePowerW) >= 1000) ? `${this.convertToKillo(dischargePowerW, dp)} kW` : `${dischargePowerW} W`; - states.batteryPower.displayUnit = (Math.abs(batteryPowerW) >= 1000) ? 'kW' : 'W'; - - states.batteryPowerReserveEnergy.display = (Math.abs(batteryPowerReserveEnergyWh) >= 1000) ? this.convertToKillo(batteryPowerReserveEnergyWh, dp) : batteryPowerReserveEnergyWh; - states.batteryPowerReserveEnergy.displayStr = (Math.abs(batteryPowerReserveEnergyWh) >= 1000) ? `${this.convertToKillo(batteryPowerReserveEnergyWh, dp)} kWh` : `${batteryPowerReserveEnergyWh} Wh`; - - states.socEnergy.display = (Math.abs(socEnergyWh) >= 1000) ? socEnergyKWh : socEnergyWh; - states.socEnergy.displayStr = (Math.abs(socEnergyWh) >= 1000) ? `${socEnergyKWh} kWh` : `${socEnergyWh} Wh`; - - states.chargeRate.display = (Math.abs(chargeRateW) >= 1000) ? this.convertToKillo(chargeRateW, dp) : chargeRateW; - states.chargeRate.displayStr = (Math.abs(chargeRateW) >= 1000) ? `${this.convertToKillo(chargeRateW, dp)} kW` : `${chargeRateW} W`; - states.dischargeRate.display = (Math.abs(dischargeRateW) >= 1000) ? this.convertToKillo(dischargeRateW, dp) : dischargeRateW; - states.dischargeRate.displayStr = (Math.abs(dischargeRateW) >= 1000) ? `${this.convertToKillo(dischargeRateW, dp)} kW` : `${dischargeRateW} W`; + display = (Math.abs(value) >= 1000) ? ((displayAbsPower) ? Math.abs(displayK) : displayK) : ((displayAbsPower) ? Math.abs(value) : value); + displayStr = (Math.abs(value) >= 1000) ? `${(displayAbsPower) ? Math.abs(displayK) : displayK} ${displayKUom}` : `${(displayAbsPower) ? Math.abs(value) : value} ${displayUom}`; + displayUnit = (Math.abs(value) >= 1000) ? displayKUom : displayUom; break; } - this.calculatedStates = states - + return { + rawState: state, + uom: uom, + value: value, + kValue: kValue, + display: display, + displayStr: displayStr, + displayUnit: displayUnit, + } } - renderReserveAndCapacity(): TemplateResult { - return html` -
- Capacity: ${this.calculatedStates.batteryCapacity.displayStr} | Reserve: ${this.calculatedStates.batteryPowerReserveEnergy.displayStr} (${this.calculatedStates.batteryPowerReservePercent.displayStr}) -
- `; - } + calculateStats(): void { + const useCustomDod = (this.config.use_custom_dod !== undefined) ? this.config.use_custom_dod : USE_CUSTOM_DOD; + const customDod = (this.config.custom_dod !== undefined) ? this.config.custom_dod : CUSTOM_DOD; + const reserveFromDod = (this.config.calculate_reserve_from_dod !== undefined) ? this.config.calculate_reserve_from_dod : CALCULATE_RESERVE_FROM_DOD; - renderRates(): TemplateResult { + const states = {}; - const rates = html` -
- Max. Charge Rate: ${this.calculatedStates.chargeRate.displayStr} | Max. Discharge Rate: ${this.calculatedStates.dischargeRate.displayStr} -
- `; + states.socPercent = this.getPercentageStats(this._getSocEntity); + states.batteryPowerReservePercent = this.getPercentageStats(this._getBatteryPowerReserve); + states.batteryPower = this.getGivTcpStats(this._getBatteryPowerEntity.state, this._getBatteryPowerEntity.attributes?.unit_of_measurement); + states.dischargePower = this.getGivTcpStats(this._getDischargePowerEntity.state, this._getDischargePowerEntity.attributes?.unit_of_measurement); + states.chargePower = this.getGivTcpStats(this._getChargePowerEntity.state, this._getChargePowerEntity.attributes?.unit_of_measurement); + states.chargeRate = this.getGivTcpStats(this._getBatteryChargeRate.state, this._getBatteryChargeRate.attributes?.unit_of_measurement); + states.dischargeRate = this.getGivTcpStats(this._getBatteryDischargeRate.state, this._getBatteryDischargeRate.attributes?.unit_of_measurement); + + states.batteryCapacity = this.getGivTcpStats(this._getBatteryCapacityKwhEntity.state, DISPLAY_UNITS.KWH); + states.socEnergy = this.getGivTcpStats(this._getSocKwhEntity.state, this._getSocKwhEntity.attributes?.unit_of_measurement); + + // post process DOD + let dod = (useCustomDod) ? Math.abs(parseFloat(customDod)) / 100.0 : 1.0; + if(dod > 1) { + dod = 1.0 + } + + const soc = states.socPercent.value / 100.0; + const usableWh = Math.round(states.batteryCapacity.value * dod); + + states.usableBatteryCapacity = this.getGivTcpStats(usableWh.toString(), DISPLAY_UNITS.WH); + const socWh = Math.round(usableWh * soc); + states.calculatedSocEnergy = this.getGivTcpStats(socWh.toString(), DISPLAY_UNITS.WH); + + let batteryPowerReserveEnergyWh = Math.round(states.batteryCapacity.value * (states.batteryPowerReservePercent.value / 100)) + + if(reserveFromDod) { + batteryPowerReserveEnergyWh = Math.round(usableWh * (states.batteryPowerReservePercent.value / 100)) + } - let pbColour = ""; - let cl = "progress-bar-fill-n0"; + states.batteryPowerReserveEnergy = this.getGivTcpStats(batteryPowerReserveEnergyWh.toString(), DISPLAY_UNITS.WH); + + // Usage charge/discharge as % of rates let perc = 0; let dispPerc = 0; let p = 0; let r = 0; - if (this.calculatedStates.batteryPower.w > 0) { - pbColour = "r"; - p = this.calculatedStates.dischargePower.w - r = this.calculatedStates.dischargeRate.w + if (states.batteryPower.value > 0) { + p = states.dischargePower.value + r = states.dischargeRate.value } - if (this.calculatedStates.batteryPower.w < 0) { - pbColour = "g"; - p = this.calculatedStates.chargePower.w - r = this.calculatedStates.chargeRate.w + if (states.batteryPower.value < 0) { + p = states.chargePower.value + r = states.chargeRate.value } if(p > 0 && r > 0) { perc = (p / r) * 100; - const pDp = (perc < 0.1) ? 2 : 1; - dispPerc = this.roundPercentage(perc, pDp); - cl = `progress-bar-fill-${pbColour}${Math.floor(dispPerc / 10) * 10}`; } + dispPerc = this.roundPercentage(perc, ((perc < 0.1) ? 2 : 1)); + + states.batteryUsageRatePercent = { + rawState: perc.toString(), + uom: "%", + value: perc, + kValue: perc, + display: (dispPerc > 100) ? 100 : dispPerc, + displayStr: `${(dispPerc > 100) ? 100 : dispPerc}%`, + displayUnit: "%", + } + + this.calculatedStates = states; + } + + renderReserveAndCapacity(): TemplateResult { + + const useCustomDod = (this.config.use_custom_dod !== undefined) ? this.config.use_custom_dod : USE_CUSTOM_DOD; + const customDod = (this.config.custom_dod !== undefined) ? this.config.custom_dod : CUSTOM_DOD; + const displayCustomDod = (this.config.display_custom_dod_stats !== undefined) ? this.config.display_custom_dod_stats : DISPLAY_CUSTOM_DOD_STATS; + + let dod = html``; + let capacityPrefix = ""; + if(useCustomDod && displayCustomDod) { + capacityPrefix = "Usable" + dod = html` +
+ DoD: ${customDod}% | Actual Capacity: ${this.calculatedStates.batteryCapacity.displayStr} +
` + } + + return html` +
+
+ ${capacityPrefix} Capacity: ${this.calculatedStates.usableBatteryCapacity.displayStr} | Reserve: ${this.calculatedStates.batteryPowerReserveEnergy.displayStr} (${this.calculatedStates.batteryPowerReservePercent.displayStr}) +
+ ${dod} +
+ `; + } + + renderRates(): TemplateResult { + + const rates = html` +
+ Max. Charge Rate: ${this.calculatedStates.chargeRate.displayStr} | Max. Discharge Rate: ${this.calculatedStates.dischargeRate.displayStr} +
+ `; + + const pbColour = (this.calculatedStates.batteryPower.value > 0) ? "r" : "g"; + const cl = `progress-bar-fill-${pbColour}${Math.ceil(this.calculatedStates.batteryUsageRatePercent.value / 10) * 10}`; + return html`
- ${this._getBatteryStatus} @ ${dispPerc}% max. rate + ${this._getBatteryStatus} @ ${this.calculatedStates.batteryUsageRatePercent.displayStr} max. rate
- +
@@ -475,12 +399,12 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { let powerColourClass = ''; let powerSubtitle = html``; - if (this.calculatedStates.batteryPower.w > 0) { + if (this.calculatedStates.batteryPower.value > 0) { powerColourClass = 'battery-power-out'; powerSubtitle = html``; } - if (this.calculatedStates.batteryPower.w < 0) { + if (this.calculatedStates.batteryPower.value < 0) { powerColourClass = 'battery-power-in'; powerSubtitle = html``; } @@ -503,7 +427,7 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { renderStats(): TemplateResult[] { const statsList: TemplateResult[] = []; - const power = this.calculatedStates.batteryPower.w; + const power = this.calculatedStates.batteryPower.value; const estIcon = html`` const timeIcon = html`` @@ -745,7 +669,7 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { data-entity-id="${`sensor.${this._getSensorPrefix}soc_kwh`}" id="gtpc-battery-detail-soc-kwh-text" > - ${this.calculatedStates.socEnergy.displayStr} + ${this.calculatedStates.calculatedSocEnergy.displayStr} ${this.renderPowerUsage()} @@ -838,10 +762,10 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { private get _getEstimatedTimeLeft(): number { let timeSecs = 0; - const socWatts = this.calculatedStates.socEnergy.Wh; - const capacity = this.calculatedStates.batteryCapacity.Wh; + const socWatts = this.calculatedStates.calculatedSocEnergy.value; + const capacity = this.calculatedStates.usableBatteryCapacity.value; const reserve = this.calculatedStates.batteryPowerReservePercent.value / 100; - const dischargePower = this.calculatedStates.dischargePower.w; + const dischargePower = this.calculatedStates.dischargePower.value; const reserveWatts = capacity * reserve; const socWattsLessReserve = socWatts - reserveWatts; @@ -856,9 +780,9 @@ export class GivTCPBatteryCard extends LitElement implements LovelaceCard { private get _getEstimatedChargeTime(): number { let timeSecs = 0; - const chargePower = this.calculatedStates.chargePower.w; - const socWatts = this.calculatedStates.socEnergy.Wh; - const capacityWatts = this.calculatedStates.batteryCapacity.Wh; + const chargePower = this.calculatedStates.chargePower.value; + const socWatts = this.calculatedStates.calculatedSocEnergy.value; + const capacityWatts = this.calculatedStates.usableBatteryCapacity.value; if (chargePower > 0) { const socDiff = capacityWatts - socWatts; diff --git a/src/style.ts b/src/style.ts index 71a0858..a50de00 100644 --- a/src/style.ts +++ b/src/style.ts @@ -58,6 +58,10 @@ export const styleCss = css` } .status-text-small { + color: var(--vc-secondary-text-color); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; font-size: 12px; } @@ -182,83 +186,83 @@ export const styleCss = css` background-color: rgba(0,0,0,0); } - .progress-bar-fill-r0 { + .progress-bar-fill-r10 { background-color: #DB4437ff; } - .progress-bar-fill-r10 { + .progress-bar-fill-r20 { background-color: #CD3C31ff; } - .progress-bar-fill-r20 { + .progress-bar-fill-r30 { background-color: #BF352Bff; } - .progress-bar-fill-r30 { + .progress-bar-fill-r40 { background-color: #B12D25ff; } - .progress-bar-fill-r40 { + .progress-bar-fill-r50 { background-color: #A3261Fff; } - .progress-bar-fill-r50 { + .progress-bar-fill-r60 { background-color: #961E18ff; } - .progress-bar-fill-r60 { + .progress-bar-fill-r70 { background-color: #881712ff; } - .progress-bar-fill-r70 { + .progress-bar-fill-r80 { background-color: #7A0F0Cff; } - .progress-bar-fill-r80 { + .progress-bar-fill-r90 { background-color: #6C0806ff; } - .progress-bar-fill-r90 { + .progress-bar-fill-r100 { background-color: #5E0000ff; } - .progress-bar-fill-g0 { + .progress-bar-fill-g10 { background-color: #43A047ff; } - .progress-bar-fill-g10 { + .progress-bar-fill-g20 { background-color: #3C9642ff; } - .progress-bar-fill-g20 { + .progress-bar-fill-g30 { background-color: #348C3Cff; } - .progress-bar-fill-g30 { + .progress-bar-fill-g40 { background-color: #2D8237ff; } - .progress-bar-fill-g40 { + .progress-bar-fill-g50 { background-color: #257832ff; } - .progress-bar-fill-g50 { + .progress-bar-fill-g60 { background-color: #1E6D2Cff; } - .progress-bar-fill-g60 { + .progress-bar-fill-g70 { background-color: #166327ff; } - .progress-bar-fill-g70 { + .progress-bar-fill-g80 { background-color: #0F5922ff; } - .progress-bar-fill-g80 { + .progress-bar-fill-g90 { background-color: #074F1Cff; } - .progress-bar-fill-g90 { + .progress-bar-fill-g100 { background-color: #004517ff; } diff --git a/src/types.ts b/src/types.ts index c228124..6823907 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,80 +1,25 @@ +export interface GivTcpStats { + rawState: string, // store raw value from GivTCP + uom: string | undefined, // unit_of_measurement from GivTCP + value: number, + kValue: number, + display: number, + displayStr: string, + displayUnit: string | undefined, +} export interface GivTcpBatteryStats { - socPercent: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - value: number, - display: number, - displayStr: string, - }, - batteryPower: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - w: number, - kW: number, - display: number, - displayStr: string, - displayUnit: string, - }, - socEnergy: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - Wh: number, - kWh: number, - display: number, - displayStr: string, - }, - dischargePower: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - w: number, - kW: number, - display: number, - displayStr: string, - }, - chargePower: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - w: number, - kW: number, - display: number, - displayStr: string, - }, - batteryCapacity: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - Wh: number, - kWh: number, - display: number, - displayStr: string, - }, - batteryPowerReservePercent: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - value: number, - display: number, - displayStr: string, - }, - batteryPowerReserveEnergy: { - Wh: number, - kWh: number, - display: number, - displayStr: string, - }, - chargeRate: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - w: number, - kW: number, - display: number, - displayStr: string, - }, - dischargeRate: { - rawState: string, // store raw value from GivTCP - uom: string | undefined, // unit_of_measurement form GivTCP - w: number, - kW: number, - display: number, - displayStr: string, - }, + socPercent: GivTcpStats, + batteryPower: GivTcpStats, + socEnergy: GivTcpStats, + dischargePower: GivTcpStats, + chargePower: GivTcpStats, + batteryCapacity: GivTcpStats, + batteryPowerReservePercent: GivTcpStats, + batteryPowerReserveEnergy: GivTcpStats, + chargeRate: GivTcpStats, + dischargeRate: GivTcpStats, + usableBatteryCapacity: GivTcpStats, + calculatedSocEnergy: GivTcpStats, + batteryUsageRatePercent: GivTcpStats, }