Skip to content
Merged
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
17 changes: 17 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
the `0` read like a measured "not faulted". Values the gateway reports are
unchanged. Telegraf skips `null` fields, and no Powerwall-Dashboard panel
uses these flags. Same fix as proxy t115 (pypowerwall#179)
- **Power Flow battery percentage at low state of charge** - the SoE label is
bottom-aligned inside the battery graphic when it would otherwise overflow,
so values such as `4%` are no longer cut off at the bottom of the Power Flow
card.
- **Power Flow grid and home labels stay above their icons on narrow cards** -
the labels were anchored to the edges of the diagram, so the right-hand label
could run past the card. They are now centered over the respective icons, and
the two circles move closer together on narrow iframes instead of running
outside the card.
- **Gateway firmware stays readable on narrow Console cards** - the mode and
firmware badges no longer split across lines or squeeze the connection
status; the status sits with the host/IP line, and gateway cards stay wide
enough when multiple gateways share a row.
- **Console MQTT and control status pills are optically centered** - the
`Connected` and `Control enabled` labels use a centered flex line box instead
of sitting slightly low inside the pill; gateway mode/firmware tags and the
active header link use the same optical alignment.

### [0.16.0] - 2026-11-10

Expand Down
8 changes: 7 additions & 1 deletion app/static/css/page.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,18 @@ header {
}

.header-links a {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 36px;
color: var(--text-secondary);
text-decoration: none;
padding: 8px 16px;
padding: 0 16px;
border-radius: 6px;
transition: all 0.2s ease;
font-size: 0.9em;
line-height: 1;
box-sizing: border-box;
}

.header-links a:hover,
Expand Down
52 changes: 43 additions & 9 deletions app/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,16 @@
background: rgba(248, 81, 73, 0.15);
color: var(--accent-red);
}
.mqtt-status-badge,
.control-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 23px;
padding: 0 10px;
line-height: 1;
box-sizing: border-box;
}
.mqtt-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
Expand All @@ -1405,14 +1415,16 @@

.gateway-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
gap: 16px;
}

.gateway-item {
display: flex;
justify-content: space-between;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
column-gap: 12px;
row-gap: 4px;
padding: 16px;
background: var(--bg-secondary);
border-radius: 8px;
Expand All @@ -1433,25 +1445,33 @@
}

.gateway-info {
display: flex;
flex-direction: column;
gap: 4px;
display: contents;
}

.gateway-name {
grid-column: 1 / -1;
grid-row: 1;
min-width: 0;
font-weight: 600;
font-size: 1em;
overflow-wrap: anywhere;
}

.gateway-host {
grid-column: 1;
grid-row: 2;
min-width: 0;
font-size: 0.85em;
color: var(--text-muted);
overflow-wrap: anywhere;
}

.gateway-details {
display: flex;
flex-wrap: wrap;
grid-column: 1 / -1;
grid-row: 3;
gap: 8px;
margin-top: 4px;
}

.gateway-mode {
Expand All @@ -1460,6 +1480,7 @@
border-radius: 4px;
background: var(--bg-card);
color: var(--text-secondary);
white-space: nowrap;
}

.gateway-firmware {
Expand All @@ -1468,10 +1489,23 @@
border-radius: 4px;
background: rgba(88, 166, 255, 0.15);
color: var(--accent-blue);
white-space: nowrap;
}


.gateway-mode,
.gateway-firmware {
display: inline-flex;
align-items: center;
min-height: 20px;
padding: 1px 8px 3px;
line-height: 1;
box-sizing: border-box;
}

.gateway-status {
display: flex;
grid-column: 2;
grid-row: 2;
align-items: center;
gap: 8px;
}
Expand Down Expand Up @@ -1503,9 +1537,9 @@
}

.gateway-error {
grid-column: 1 / -1;
font-size: 0.75em;
color: var(--accent-red);
margin-top: 4px;
}

/* Footer */
Expand Down
36 changes: 28 additions & 8 deletions app/static/powerflow/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -23722,6 +23722,7 @@
ft(this, "_handleIslandingClick", this._handleIslandingClick.bind(this)),
(this.state = {
activeIcons: [],
viewportWidth: window.innerWidth,
colors: {
batteryToHome: et.c,
batteryToGrid: et.c,
Expand All @@ -23739,9 +23740,20 @@
componentWillReceiveProps(e) {
this.setState({ activeIcons: bt.getActiveIcons(e) });
}
componentDidMount() {
this._handleViewportResize = () => this.setState({ viewportWidth: window.innerWidth });
window.addEventListener("resize", this._handleViewportResize);
}
componentWillUnmount() {
window.removeEventListener("resize", this._handleViewportResize);
}
_getEffectiveGridWidth() {
// Keep 68px per side for the grid and home circles, which sit
// outside the diagram's edges, so they fit narrow iframes
return Math.min(this.props.gridWidth, Math.max(0, window.innerWidth - 136));
}
render() {
const {
gridWidth: e,
gridHeight: t,
activeTypes: i,
activeGlowRadius: n,
Expand Down Expand Up @@ -23792,6 +23804,7 @@
soe: K,
} = this.props,
{ activeIcons: Y, colors: Q } = this.state,
e = this._getEffectiveGridWidth(),
Z = 0 === i.length,
J = e / 2,
X = t / 2,
Expand Down Expand Up @@ -24290,8 +24303,9 @@
case et.h.GRID:
((b = this.props.gridPower && f),
(I.top = g ? 0.5 * s + 0.5 * o + 2 * _ : 1.5 * s + 0.5 * o + 3 * _ + T + l + d),
(I.left = 0),
(I.right = a + s + 2 * _),
(I.left = `calc(50% - ${a / 2 + s / 2 + _}px)`),
(I.right = "auto"),
(I.transform = "translateX(-50%)"),
(I.color = et.d));
break;
case et.h.BATTERY:
Expand All @@ -24305,8 +24319,9 @@
case et.h.USAGE:
((b = this.props.loadPower && v),
(I.top = g ? 0.5 * s + 0.5 * o + 2 * _ : 1.5 * s + 0.5 * o + 3 * _ + T + l + d),
(I.left = a + s + 2 * _),
(I.right = 0),
(I.left = `calc(50% + ${a / 2 + s / 2 + _}px)`),
(I.right = "auto"),
(I.transform = "translateX(-50%)"),
(I.color = et.g));
}
const O = !t.includes(n),
Expand Down Expand Up @@ -24386,6 +24401,9 @@
s = e ? 267 : 94,
_ = e ? 212 : 94,
l = e ? 172 : 157;
const labelTop = n + (_ * (100 - a)) / 100 - i / 2,
labelBottomAligned = labelTop > s - i - 1,
gridWidth = this._getEffectiveGridWidth();
((d = c.createElement(
"svg",
{ className: "powerwall-soe", width: r, height: s, viewBox: `0 0 ${r} ${s}` },
Expand All @@ -24410,10 +24428,12 @@
"data-testid": "234320a0-eab5-4528-bc42-aab6c7282f13",
className: "label-container soe-label",
style: {
top: Math.max(0, n + (_ * (100 - a)) / 100 - i / 2),
top: labelBottomAligned ? "auto" : Math.max(0, labelTop),
bottom: labelBottomAligned ? 0 : "auto",
lineHeight: labelBottomAligned ? "normal" : void 0,
left: e
? 0.5 * this.props.gridWidth - t + 191
: r + (0.5 * this.props.gridWidth - t) + 12,
? 0.5 * gridWidth - t + 191
: r + (0.5 * gridWidth - t) + 12,
color: o ? "white" : "black",
},
},
Expand Down
Loading