Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
13eb0d5
Add smart feedin feature
andig Jun 12, 2025
89fa060
Add feedin limit
andig Jun 12, 2025
44b326d
Update apis
andig Jun 12, 2025
cf8fca8
Implement PV mode disable
andig Jun 12, 2025
9b4fb0e
wip
andig Jun 12, 2025
214c197
wip
andig Jun 12, 2025
2273cd6
Update core/loadpoint.go
andig Jun 12, 2025
f15c60f
Update core/loadpoint.go
andig Jun 12, 2025
33fccd8
wip
andig Jun 12, 2025
292595c
wip
andig Jun 12, 2025
267d248
Merge branch 'master' into feat/smartfeedin
andig Jun 12, 2025
8e943ad
Revert rename
andig Jun 13, 2025
2a579ea
wip
andig Jun 13, 2025
a9bcf01
Move isDynamicTariff to backend
andig Jun 13, 2025
27f0ae1
wip
andig Jun 13, 2025
6968d3e
wip
andig Jun 13, 2025
85d9d51
wip
andig Jun 13, 2025
3944218
Merge branch 'feat/smartfeedin' into feat/zero-feedin
andig Jun 14, 2025
8844e35
Implement smart feed-in limit
andig Jun 14, 2025
defa184
Add shutdown hook
andig Jun 14, 2025
eb7831a
wip
andig Jun 14, 2025
1228dce
wip
andig Jun 14, 2025
67de209
wip
andig Jun 14, 2025
0b1c1eb
wip
andig Jun 14, 2025
48e865f
Merge branch 'feat/smartfeedin' into feat/zero-feedin
andig Jun 14, 2025
580043b
Merge branch 'master' into feat/zero-feedin
naltatis Jun 14, 2025
8f894d2
Merge branch 'master' into feat/smartfeedin
naltatis Jun 21, 2025
73de54a
replace ui logic with smartCostAvailable
naltatis Jun 21, 2025
97c727e
rename smartFeedin* to smartFeedinPriority*, introduce smartFeedinAva…
naltatis Jun 21, 2025
0a29468
Rename SmartFeedinPriorityAvailable
andig Jun 21, 2025
cd73358
Merge branch 'master' into feat/smartfeedin
andig Jun 21, 2025
083622c
Merge branch 'feat/smartfeedin' into feat/zero-feedin
andig Jun 21, 2025
58612a3
Merge branch 'feat/zero-feedin' of https://github.com/evcc-io/evcc in…
andig Jun 21, 2025
529e40b
smart cost limit ui refactoring; add smart feedin priority basics
naltatis Jun 21, 2025
e8f497c
adjust to rename smartFeedinPriorityAvailable
naltatis Jun 21, 2025
160ba55
Merge branch 'master' into feat/smartfeedin
naltatis Jun 21, 2025
2b97ad6
wip
andig Jun 21, 2025
51e455b
status (wip)
naltatis Jun 23, 2025
1c741c2
e2e (wip); modal refactor
naltatis Jun 23, 2025
8d11e03
clean
naltatis Jun 23, 2025
d84393a
add loadpoint status ui
naltatis Jun 24, 2025
57e1111
refactor status icons
naltatis Jun 25, 2025
cbf95b1
add e2e
naltatis Jun 25, 2025
08e3776
fix battery grid charge e2e
naltatis Jun 25, 2025
370cd0b
Merge branch 'master' into feat/smartfeedin
naltatis Jun 25, 2025
59cb98c
use playwright worker count auto
naltatis Jun 25, 2025
d6618bc
playwright: add html report
naltatis Jun 25, 2025
9eb5853
Use FeedIn as name, remove duplicated functions
andig Jun 25, 2025
81733d3
Merge branch 'master' into feat/smartfeedin
andig Jun 25, 2025
3e4ad7c
Follow ref changes
andig Jun 25, 2025
a9d0f1e
Merge branch 'master' into feat/zero-feedin
naltatis Jun 27, 2025
197d529
git case rename foo
naltatis Jun 27, 2025
a77635b
git case rename foo
naltatis Jun 27, 2025
b069b3b
git case rename foo
naltatis Jun 27, 2025
a30a780
git case rename foo
naltatis Jun 27, 2025
283d8d1
Fix conflict
andig Jun 28, 2025
4d1d7d2
wip
andig Jun 28, 2025
84ec766
wip
andig Jun 28, 2025
4499885
Merge branch 'master' into feat/zero-feedin
andig Jul 2, 2025
172ad72
Merge branch 'master' into feat/zero-feedin
naltatis Jul 9, 2025
a113ec8
Merge branch 'master' into feat/zero-feedin
naltatis Aug 15, 2025
b321909
Merge branch 'master' into feat/zero-feedin
naltatis Aug 20, 2025
a8b8360
implement smart feedin limit ui
naltatis Aug 20, 2025
4b5e23d
Merge branch 'master' into feat/zero-feedin
andig Aug 25, 2025
332cbd4
fix build
naltatis Aug 25, 2025
ded72b0
add feedin disabled highlighting
naltatis Aug 25, 2025
0d86ff5
lint fix
naltatis Aug 25, 2025
8fa048c
generate options; refactor
naltatis Aug 26, 2025
cc7b5b8
Merge branch 'master' into feat/zero-feedin
naltatis Aug 26, 2025
8788298
Merge branch 'master' into feat/zero-feedin
naltatis Aug 27, 2025
fde7e8c
show reduced status in energy flow; naming consistancy
naltatis Aug 27, 2025
3d8e0c5
add e2e
naltatis Aug 27, 2025
86abcf2
revert demo
naltatis Aug 27, 2025
0cd6dff
naming
naltatis Aug 27, 2025
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
39 changes: 38 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ This file provides guidance to AI coding agents when working with code in this r
- Use early returns for readability
- Use configured Axios instance for HTTP communication

### Code Comments

- Keep code comments as brief as possible
- Start comments with lowercase letters
- Avoid JSDoc comments - prefer clear code and TypeScript types
- Use inline comments sparingly, only when the code intent isn't clear from context

### State Management

- Use `reactive()` from Vue for simple global state
Expand Down Expand Up @@ -167,6 +174,25 @@ This file provides guidance to AI coding agents when working with code in this r
- Use semantic selectors (roles, labels, button text); `data-testid` only when necessary
- Test error states and loading states

### JavaScript Unit Testing (Vitest)

- **Concrete Assertions**: Use specific expected values instead of calculations in tests
- Good: `expect(values.slice(0, 3)).toEqual([-0.05, -0.045, -0.04])`
- Bad: `expect(values).toContain(Math.min(...inputValues) - stepSize)`
- **Minimal Logic**: Avoid complex logic in tests; verify exact outputs
- Use helper functions for test setup, not for expected value calculations
- Check actual function output against known correct values
- **Test Naming**: Use descriptive names that reflect the feature being tested
- Pattern: `featureName: describes what is being tested`
- Examples: `extraLow: extends range below minimum`, `formatValue: converts euro to cents`
- **Edge Cases**: Always test boundary conditions
- Empty inputs, single values, extreme ranges
- Different step sizes, negative values, zero handling
- **Programmatic Execution**:
- Run all tests: `npm run test` (watch mode) or `npx vitest run` (single run)
- Run specific test file: `npx vitest run filename`
- Run tests matching pattern: `npx vitest run --grep "pattern"`

## Playwright Integration Testing

### Test Organization
Expand Down Expand Up @@ -208,7 +234,18 @@ This file provides guidance to AI coding agents when working with code in this r
- Use `expectModalVisible()` and `expectModalHidden()` helpers
- Test configuration persistence across application restarts
- Standard structure: import `{ start, stop, baseUrl }` from `./evcc`, use `test.afterEach(stop)`
- Never use fixed timeouts, use existance of elements or wait for network idle
- **NEVER use fixed timeouts like `waitForTimeout()`** - Playwright's auto-waiting handles synchronization
- Use Playwright's built-in waiting mechanisms: `expect()` assertions automatically wait for conditions
- For dynamic content, rely on element visibility/text content assertions rather than arbitrary delays
- **Comments**: Keep test comments very concise, lowercase, only where needed to clarify intent
- **No step numbering**: Avoid numbered steps or excessive structure in comments
- Combine related test scenarios into single comprehensive flows to reduce redundant setup
- **Be compact**: Don't check visibility before interacting - Playwright handles this automatically
- **Use concrete values**: In controlled test environments with mocked data, assert exact expected values
- **Avoid regex in assertions**: Use `toContainText("exact string")` not `toContainText(/regex/)`
- **Custom select boxes**: Use `selectOption({ label: "exact label" })` to verify option formatting
- **Minimize variables**: Use inline selectors unless reused multiple times
- **Descriptive test names**: Be brief but specific about what the test covers (e.g., "configure feed-in limit and verify production reduced indicator")

## Device Integration & Configuration

Expand Down
5 changes: 5 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ type SocLimiter interface {
GetLimitSoc() (int64, error)
}

// FeedInDisableController allows disabling grid feed-in, i.e. setting limit to 0W
type FeedInDisableController interface {
FeedInDisableLimitEnable(bool) error
}

// ChargeController allows to start/stop the charging session on the vehicle side
type ChargeController interface {
ChargeEnable(bool) error
Expand Down
14 changes: 12 additions & 2 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
--evcc-darkest-green: #076f20ff;
--evcc-darkest-green-rgb: 7, 111, 32;
--evcc-yellow: #faf000;
--evcc-dark-yellow: #bbb400;
--evcc-dark-yellow: #e6b800;
--evcc-orange: #ff9000;
--evcc-orange-rgb: 255, 144, 0;
--evcc-red: #fc440f;
Expand All @@ -46,7 +46,8 @@
--evcc-self: var(--evcc-dark-green);
--evcc-pv: var(--evcc-dark-green);
--evcc-battery: var(--evcc-darker-green);
--evcc-export: var(--evcc-yellow);
--evcc-export: var(--evcc-dark-yellow);
--evcc-export-bg: var(--evcc-yellow);
--evcc-price: #ff912fff;
--evcc-co2: #00916eff;
--evcc-background: var(--bs-gray-bright);
Expand Down Expand Up @@ -92,6 +93,7 @@

:root.dark {
--evcc-grid: var(--bs-gray-medium);
--evcc-export: var(--evcc-yellow);
--evcc-background: var(--bs-gray-deep);
--evcc-box: var(--bs-gray-dark);
--evcc-box-border: var(--bs-gray-darker);
Expand Down Expand Up @@ -172,6 +174,10 @@ h5 {
color: var(--evcc-price) !important;
}

.text-export {
color: var(--evcc-export) !important;
}

.text-co2 {
color: var(--evcc-co2) !important;
}
Expand Down Expand Up @@ -682,3 +688,7 @@ input::-webkit-date-and-time-value {
text-overflow: ellipsis;
}
}

.custom-select-inline {
display: inline-block !important;
}
50 changes: 50 additions & 0 deletions assets/js/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const colors: {
pricePerKWh: string | null;
price: string | null;
co2: string | null;
export: string | null;
background: string | null;
light: string | null;
selfPalette: string[];
Expand All @@ -31,6 +32,7 @@ const colors: {
pricePerKWh: null,
price: null,
co2: null,
export: null,
background: null,
light: null,
selfPalette: ["#0FDE41FF", "#FFBD2FFF", "#FD6158FF", "#03C1EFFF", "#0F662DFF", "#FF922EFF"],
Expand Down Expand Up @@ -62,6 +64,53 @@ export const fullColor = (color: string | null) => {
return color?.toLowerCase().replace(/20$/, "ff");
};

export const createFeedInDisabledPattern = (
ctx: CanvasRenderingContext2D,
color: string,
opacity = 0.5
): CanvasPattern | null => {
const patternCanvas = document.createElement("canvas");
const patternContext = patternCanvas.getContext("2d");
if (!patternContext) return null;

// Set pattern size
patternCanvas.width = 10;
patternCanvas.height = 10;

// Create diagonal stripes with more saturation
patternContext.strokeStyle = color;
patternContext.globalAlpha = opacity;
patternContext.lineWidth = 3;

// Draw diagonal lines
patternContext.beginPath();
patternContext.moveTo(0, 10);
patternContext.lineTo(10, 0);
patternContext.stroke();

patternContext.beginPath();
patternContext.moveTo(-2, 2);
patternContext.lineTo(2, -2);
patternContext.stroke();

patternContext.beginPath();
patternContext.moveTo(8, 12);
patternContext.lineTo(12, 8);
patternContext.stroke();

return ctx.createPattern(patternCanvas, "repeat");
};

export const feedInDisabledPattern = (color: string): string => {
return `repeating-linear-gradient(
-45deg,
transparent,
transparent 3px,
${color} 3px,
${color} 5px
)`;
};

function updateCssColors() {
const style = window.getComputedStyle(document.documentElement);
colors.text = style.getPropertyValue("--evcc-default-text");
Expand All @@ -71,6 +120,7 @@ function updateCssColors() {
colors.grid = style.getPropertyValue("--evcc-grid");
colors.price = style.getPropertyValue("--evcc-price");
colors.co2 = style.getPropertyValue("--evcc-co2");
colors.export = style.getPropertyValue("--evcc-export");
colors.background = style.getPropertyValue("--evcc-background");
colors.pricePerKWh = style.getPropertyValue("--bs-gray-medium");
colors.co2PerKWh = style.getPropertyValue("--bs-gray-medium");
Expand Down
3 changes: 0 additions & 3 deletions assets/js/components/Battery/BatterySettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,4 @@ export default defineComponent({
z-index: 1;
border-radius: 0.5rem;
}
.custom-select-inline {
display: inline-block !important;
}
</style>
14 changes: 11 additions & 3 deletions assets/js/components/Energyflow/Energyflow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<div>
<EnergyflowEntry
v-if="pvPossible"
:name="$t('main.energyflow.pvProduction')"
:name="pvTitle"
icon="sun"
:power="pvProduction"
:details="solarForecastRemainingToday"
Expand Down Expand Up @@ -334,6 +334,7 @@ export default defineComponent({
bufferSoc: { type: Number },
bufferStartSoc: { type: Number },
forecast: { type: Object as PropType<Forecast>, default: () => ({}) },
smartFeedInDisableActive: { type: Boolean },
},
data: () => {
return { detailsOpen: false, detailsCompleteHeight: null as number | null, ready: false };
Expand All @@ -342,6 +343,13 @@ export default defineComponent({
gridImport() {
return Math.max(0, this.gridPower);
},
pvTitle() {
let title = this.$t("main.energyflow.pvProduction");
if (this.smartFeedInDisableActive) {
title += ` (${this.$t("main.energyflow.pvProductionLimited")})`;
}
return title;
},
pvProduction() {
return Math.abs(this.pvPower);
},
Expand Down Expand Up @@ -607,13 +615,13 @@ export default defineComponent({
color: var(--evcc-grid);
}
.color-export {
color: var(--evcc-export);
color: var(--evcc-export-bg);
}
.legend-grid {
color: var(--evcc-grid);
}
.legend-export {
color: var(--evcc-export);
color: var(--evcc-export-bg);
}
.legend-pv {
color: var(--evcc-pv);
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/Energyflow/Visualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ html.dark .grid-import {
color: var(--bs-dark);
}
.pv-export {
background-color: var(--evcc-export);
background-color: var(--evcc-export-bg);
color: var(--bs-dark);
}
.unknown-power {
Expand Down
Loading
Loading