Skip to content

Commit

Permalink
feat: support split consumption/production entities (ulic75#12)
Browse files Browse the repository at this point in the history
See the readme for detail on how to configure this new feature

Closes ulic75#8
  • Loading branch information
ulic75 committed May 3, 2022
1 parent 79f84ca commit da78757
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 41 deletions.
66 changes: 47 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,60 @@ I recommend looking at the [Example usage section](#example-usage) to understand

#### Card options

| Name | Type | Default | Description |
| ------------------------- | ------ | :-----: | --------------------------------------------------------------------------------------------------------------------------------------- |
| type **_(required)_** | string | | `custom:power-distribution-card`. |
| entities **_(required)_** | map | | One or more sensor entities in a list, see [entities map](#entities-map) for additional entity options. |
| min_flow_rate | number | .75 | Represents the fastest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |
| max_flow_rate | number | 6 | Represents the slowest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |

#### Entities map

| Name | Unit | Description |
| --------------------- | :--: | --------------------------------------------------------------------------------------------------- |
| grid **_(required)_** | kW | Entity providing a state with a positive value when consuming and a negative value when producting. |
| battery | kW | Entity providing a state with a positive value when charging and a negative value when discharging. |
| battery_charge | % | Entity providing a state with the current percentage of charge on the battery. |
| solar | kW | Entity providing a state with the value of generation. |
| Name | Type | Default | Description |
| ------------- | -------- | :----------: | --------------------------------------------------------------------------------------------------------------------------------------- |
| type | `string` | **required** | `custom:power-distribution-card`. |
| entities | `object` | **required** | One or more sensor entities, see [entities object](#entities-object) for additional entity options. |
| min_flow_rate | `number` | .75 | Represents the fastest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |
| max_flow_rate | `number` | 6 | Represents the slowest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |

#### Entities object

| Name | Type | Default | Description |
| -------------- | :------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| grid | `string` / `object` | **required** | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery | `string` / `object` | | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery_charge | `string` | | Entity ID providing a state with the current percentage of charge on the battery. |
| solar | `string` | | Entity ID providing a state with the value of generation. |

#### Split entities

Can be use with either Grid or Battery configuration

| Name | Type | Default | Description |
| ----------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------- |
| consumption | `string` | **required** for grid | Entity ID providing a state value for consumption, this is required if using a split grid object. |
| production | `string` | | Entity ID providing a state value for production |

### Example usage

#### Using combined entities for grid and battery that support positive state values for consumption and negative state values for production.

```yaml
type: custom:power-distribution-card
title: Realtime Distribution
entities:
battery: sensor.battery_in_out
battery_charge: sensor.battery_percent
grid: sensor.grid_in_out
solar: sensor.solar_out
max_flow_rate: 10
```

#### Using split entities for grid and battery where each consumption and production entity state has a positive value.

```yaml
type: custom:power-distribution-card
title: Realtime Distribution
entities:
battery: sensor.powerwall_battery_now
battery_charge: sensor.powerwall_charge
grid: sensor.powerwall_site_now
solar: sensor.powerwall_solar_now
battery:
consumption: sensor.battery_out
production: sensor.battery_in
battery_charge: sensor.battery_percent
grid:
consumption: sensor.grid_out
production: sensor.grid_in
solar: sensor.solar_out
max_flow_rate: 10
```

Expand Down
14 changes: 12 additions & 2 deletions src/power-distribution-card-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ import { LovelaceCardConfig } from "custom-card-helpers";

export interface PowerDistributionCardConfig extends LovelaceCardConfig {
entities: {
battery?: string;
battery?:
| string
| {
consumption: string;
production: string;
};
battery_charge?: string;
grid?: string;
grid:
| string
| {
consumption: string;
production?: string;
};
solar?: string;
};
min_flow_rate: number;
Expand Down
68 changes: 48 additions & 20 deletions src/power-distribution-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,36 +55,59 @@ export class PowerDistributionCard extends LitElement {
return max - (value / total) * (max - min);
};

private getEntityState = (entity: string | undefined): number | null => {
if (entity === undefined) return null;
return coerceNumber(this.hass.states[entity].state, null);
private getEntityState = (entity: string | undefined): number => {
if (!entity) return 0;
return coerceNumber(this.hass.states[entity].state);
};

protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}

const hasBattery = this._config.entities.battery !== undefined;
const hasSolarProduction = this._config.entities.solar !== undefined;
const { entities } = this._config;

const hasBattery = entities.battery !== undefined;
const hasSolarProduction = entities.solar !== undefined;
const hasReturnToGrid = true;

const batteryState = this.getEntityState(this._config.entities.battery);
const batteryChargeState = this.getEntityState(
this._config.entities.battery_charge
const batteryChargeState = entities.battery_charge?.length
? this.getEntityState(entities.battery_charge)
: null;
const solarState = this.getEntityState(entities.solar);

const solarToGrid = hasReturnToGrid
? roundValue(
typeof entities.grid === "string"
? Math.abs(Math.min(this.getEntityState(entities.grid), 0))
: this.getEntityState(entities.grid.production),
1
)
: 0;

const batteryToHome = roundValue(
typeof entities.battery === "string"
? Math.max(this.getEntityState(entities.battery), 0)
: this.getEntityState(entities.battery?.consumption),
1
);

const gridToHome = roundValue(
typeof entities.grid === "string"
? Math.max(this.getEntityState(entities.grid), 0)
: this.getEntityState(entities.grid.consumption),
1
);
const gridState = this.getEntityState(this._config.entities.grid);
const solarState = this.getEntityState(this._config.entities.solar);

const solarToGrid = roundValue(Math.abs(Math.min(gridState ?? 0, 0)), 1);
const batteryToHome = roundValue(Math.max(batteryState ?? 0, 0), 1);
const gridToHome = roundValue(Math.max(gridState ?? 0, 0), 1);
const solarToBattery = roundValue(
Math.abs(Math.min(batteryState ?? 0, 0)),
typeof entities.battery === "string"
? Math.abs(Math.min(this.getEntityState(entities.battery), 0))
: this.getEntityState(entities.battery?.production),
1
);

const solarToHome =
roundValue(solarState ?? 0, 1) - solarToGrid - solarToBattery;
roundValue(solarState, 1) - solarToGrid - solarToBattery;

const homeConsumption = batteryToHome + gridToHome + solarToHome;
const totalConsumption = homeConsumption + solarToBattery + solarToGrid;
Expand Down Expand Up @@ -166,10 +189,15 @@ export class PowerDistributionCard extends LitElement {
<div class="circle-container grid">
<div class="circle">
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
<span class="return">
<ha-svg-icon class="small" .path=${mdiArrowLeft}></ha-svg-icon
>${roundValue(solarToGrid, 1)} kW
</span>
${hasReturnToGrid
? html`<span class="return">
<ha-svg-icon
class="small"
.path=${mdiArrowLeft}
></ha-svg-icon
>${roundValue(solarToGrid, 1)} kW
</span>`
: null}
<span class="consumption">
<ha-svg-icon
class="small"
Expand Down Expand Up @@ -258,7 +286,7 @@ export class PowerDistributionCard extends LitElement {
<div class="circle-container battery">
<div class="circle">
${batteryChargeState !== null
? html`<span>
? html` <span>
${formatNumber(batteryChargeState, this.hass.locale, {
maximumFractionDigits: 0,
minimumFractionDigits: 0,
Expand Down

0 comments on commit da78757

Please sign in to comment.