Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ This card is designed for the new [Home Assistant Sections UI](https://www.home-
| Curved Line Radius | `curve_factor`| Adjusts the curve radius. `0` to `5`, `1` is default. Set to `0` for straight lines. |
| Font Size Multiplier | `font_size_multiplier`| Multiplies text sizes on top of the automatic `--cpc-scale`. Default `1.0`. |
| Device Power Lines | `show_device_power_lines`| Set to `true` to light up devices when power is flowing beyond a threshold. Default `false` |
| Device sort | `device_sort`| Sort visible devices left-to-right. Options: `none`, `power_desc`, `power_asc`, `name_desc`, `name_asc`. Default `none`. |
| Home Icon Gradient | `disable_home_gradient`| Set to `true` if you want the home icon to be a single colour. |
| Remove Glow Effects | `remove_glow_effects`| Set to `true` to disable drop shadow/glow effects. Default `false` (dark mode only). |
| Hide Card Background | `hide_card_background`| Set to `true` to hide the standard HA card background. Default `false`. |
Expand Down Expand Up @@ -206,6 +207,8 @@ Devices are power feeds within your home that you want to show in the card. By d

**Important:** You can add as many devices as you want, however the display of them is determined by the card width, 12 columns = up to 8 devices, 24 columns = up to 16 devices. On small screens (phones) this will still be limited to 8 devices, unless in landscape mode where you should have more real estate.

Set the card-level `device_sort` option to order visible devices left-to-right by power or name.

| Name | Setting slug | What it does |
| ----------------------- | --------------------------------- | -------------------------------------------------------------------------------- |
| Device entity | `entity` | Sensor/entity id for a load. |
Expand Down
67 changes: 66 additions & 1 deletion compact-power-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ class CompactPowerCard extends CompactPowerCardBase {
name: "hide_card_background",
selector: { boolean: {} },
},
{
name: "device_sort",
selector: {
select: {
mode: "dropdown",
options: [
"none",
"power_desc",
"power_asc",
"name_desc",
"name_asc",
],
},
},
},
]
},
{
Expand Down Expand Up @@ -3537,18 +3552,27 @@ class CompactPowerCard extends CompactPowerCardBase {
const icon = src.icon || this._getEntityIcon(entity, "mdi:power-plug");
const isPowerDevice = this._isPowerDevice(entity);
const st = entity ? this.hass?.states?.[entity] : null;
const sortName = displayName || st?.attributes?.friendly_name || entity || "";
const raw = attribute ? st?.attributes?.[attribute] : st?.state;
const isUnavailable = this._isUnavailableState(raw);
const numeric = isUnavailable ? 0 : this._getNumericMaybe(entity, attribute);
const unit = st?.attributes?.unit_of_measurement || "";
const decimals = this._getDecimalPlaces(src);
const unitOverride = this._getUnitOverride(src);
const sortUnit = String(unit || unitOverride || "").trim().toLowerCase();
const numericW = isPowerDevice
? isUnavailable
? 0
: this._toWatts(numeric, unit, true)
: null;
const sortablePowerWatts = isUnavailable
? 0
: Number.isFinite(numericW)
? numericW
: ["w", "kw", "mw"].includes(sortUnit) && Number.isFinite(numeric)
? this._toWatts(numeric, sortUnit, true)
: null;
const hasPowerNumeric = isPowerDevice && (isUnavailable ? true : Number.isFinite(numericW));
const unitOverride = this._getUnitOverride(src);
let val = hasPowerNumeric
? this._formatPowerWithOverride(numericW, decimals, "W", unitOverride ?? null)
: this._formatEntity(entity, decimals, attribute, unitOverride);
Expand All @@ -3574,12 +3598,14 @@ class CompactPowerCard extends CompactPowerCardBase {
switchEntity,
switchOn,
name: displayName,
sortName,
icon,
val,
color,
opacity,
hidden,
numeric: hasPowerNumeric ? numericW : numeric ?? 0,
sortPower: sortablePowerWatts,
isPowerDevice,
threshold,
forceHideUnderThreshold,
Expand All @@ -3589,6 +3615,42 @@ class CompactPowerCard extends CompactPowerCardBase {
const visibleSources = deviceSources.filter(
(src) => !(src.forceHideUnderThreshold && src.hidden)
);

const deviceSort = String(this._config?.device_sort || "").toLowerCase();
if (deviceSort !== "none" && deviceSort) {
const compareDeviceNames = (a, b) =>
String(a.sortName || a.name || a.entity || "").localeCompare(String(b.sortName || b.name || b.entity || ""), undefined, {
sensitivity: "base",
numeric: true,
});

const getDeviceNumeric = (src) => {
const numeric = Number(src?.sortPower ?? src?.numeric ?? 0);
return Number.isFinite(numeric) ? numeric : 0;
};

visibleSources.sort((a, b) => {
const aNumeric = getDeviceNumeric(a);
const bNumeric = getDeviceNumeric(b);

if (deviceSort === "power_desc") {
const diff = bNumeric - aNumeric;
return diff !== 0 ? diff : compareDeviceNames(a, b);
}
if (deviceSort === "power_asc") {
const diff = aNumeric - bNumeric;
return diff !== 0 ? diff : compareDeviceNames(a, b);
}
if (deviceSort === "name_desc") {
return compareDeviceNames(b, a) || (a.sourceIndex - b.sourceIndex);
}
if (deviceSort === "name_asc") {
return compareDeviceNames(a, b) || (a.sourceIndex - b.sourceIndex);
}
return 0;
});
}

const maxDevices = Math.min(visibleSources.length, maxItemsByColumns);
const deviceVisible = visibleSources.slice(0, maxDevices);

Expand Down Expand Up @@ -3624,6 +3686,9 @@ class CompactPowerCard extends CompactPowerCardBase {
}
if (ring >= deviceRings) break;
}
if (deviceSort !== "none" && deviceSort) {
sourcePositions.sort((a, b) => a.x - b.x);
}
}

const sources = deviceVisible.map((src, idx) => {
Expand Down