Skip to content

Commit 95ffe1e

Browse files
committed
Merge remote-tracking branch 'upstream/master' into chore/plenticore-caveat
2 parents 02538f8 + 06f6eb1 commit 95ffe1e

467 files changed

Lines changed: 19848 additions & 12313 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coderabbit.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# yaml-language-server: $schema=https://storage.googleapis.com/coderabbit_public_assets/schema.v2.json
2+
reviews:
3+
# Leave the PR description untouched (no "Summary by CodeRabbit").
4+
high_level_summary: false
5+
6+
# Suppress the walkthrough comment by disabling all its parts.
7+
changed_files_summary: false
8+
sequence_diagrams: false
9+
estimate_code_review_effort: false
10+
assess_linked_issues: false
11+
related_issues: false
12+
related_prs: false
13+
poem: false
14+
15+
# Auto-apply suggested labels and reviewers to the PR.
16+
auto_apply_labels: true
17+
auto_assign_reviewers: true
18+
19+
# Flag spam / low-quality PRs (public GitHub repos only).
20+
slop_detection:
21+
enabled: true
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
name: Claude Code Review (run)
2+
3+
# Stage 2 (privileged): triggered when the "Claude Code Review" workflow above
4+
# completes. workflow_run executes in the base-repo context, so it has access
5+
# to secrets and id-token even for PRs from forks. This is what makes reviewing
6+
# external contributors' PRs possible.
7+
#
8+
# Security: the triggering workflow file is fork-controlled, so the uploaded
9+
# artifact is untrusted. We validate the PR number is purely numeric before
10+
# using it, and we only check out the base ref (never the PR head into the
11+
# workspace root) so untrusted code is never executed here. See
12+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
13+
on:
14+
workflow_run:
15+
workflows: ["Claude Code Review"]
16+
types: [completed]
17+
18+
jobs:
19+
gate:
20+
# Only react to PR runs that finished successfully.
21+
if: >
22+
github.event.workflow_run.event == 'pull_request' &&
23+
github.event.workflow_run.conclusion == 'success'
24+
runs-on: ubuntu-latest
25+
permissions:
26+
actions: read
27+
outputs:
28+
number: ${{ steps.read.outputs.number }}
29+
found: ${{ steps.check.outputs.found }}
30+
steps:
31+
- name: Check for PR-number artifact
32+
id: check
33+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
34+
with:
35+
script: |
36+
const { data } = await github.rest.actions.listWorkflowRunArtifacts({
37+
owner: context.repo.owner,
38+
repo: context.repo.repo,
39+
run_id: context.payload.workflow_run.id,
40+
});
41+
core.setOutput('found', data.artifacts.some(a => a.name === 'pr-number') ? 'true' : 'false');
42+
43+
- name: Download PR number
44+
if: steps.check.outputs.found == 'true'
45+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4
46+
with:
47+
name: pr-number
48+
run-id: ${{ github.event.workflow_run.id }}
49+
github-token: ${{ secrets.GITHUB_TOKEN }}
50+
51+
- name: Read and validate PR number
52+
id: read
53+
if: steps.check.outputs.found == 'true'
54+
run: |
55+
num="$(cat pr-number.txt)"
56+
# Artifact comes from the fork-controlled trigger workflow — never trust it blindly.
57+
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
58+
echo "Refusing to proceed: PR number is not numeric ($num)" >&2
59+
exit 1
60+
fi
61+
echo "number=$num" >> "$GITHUB_OUTPUT"
62+
63+
claude-review:
64+
needs: gate
65+
if: needs.gate.outputs.found == 'true'
66+
runs-on: ubuntu-latest
67+
permissions:
68+
contents: read
69+
pull-requests: write # post the review on the PR
70+
issues: read
71+
id-token: write
72+
statuses: write # mirror the review result onto the PR head commit
73+
concurrency:
74+
group: claude-review-run-${{ needs.gate.outputs.number }}
75+
cancel-in-progress: true
76+
steps:
77+
# workflow_run runs are detached and never show up in the PR's checks, so
78+
# publish a commit status on the PR head SHA. This surfaces a "Claude Code
79+
# Review" check next to the regular CI jobs.
80+
- name: Report review pending
81+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
82+
with:
83+
script: |
84+
await github.rest.repos.createCommitStatus({
85+
owner: context.repo.owner,
86+
repo: context.repo.repo,
87+
sha: context.payload.workflow_run.head_sha,
88+
context: 'Claude Code Review',
89+
state: 'pending',
90+
description: 'Review in progress',
91+
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
92+
});
93+
94+
# Default checkout (base ref) only — do NOT check out the untrusted PR head.
95+
- name: Checkout repository
96+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
97+
with:
98+
fetch-depth: 1
99+
100+
- name: Run Claude Code Review
101+
id: review
102+
continue-on-error: true # always report status below, even on failure
103+
uses: anthropics/claude-code-action@v1
104+
with:
105+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
106+
# Provided explicitly so the review can post on fork PRs; also required
107+
# for allowed_non_write_users to take effect.
108+
github_token: ${{ secrets.GITHUB_TOKEN }}
109+
# Review PRs from external contributors who lack write access. The diff
110+
# is fetched read-only and never executed; the subprocess env is scrubbed.
111+
allowed_non_write_users: "*"
112+
# review dependency bumps opened by Dependabot
113+
allowed_bots: "dependabot[bot]"
114+
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
115+
plugins: "code-review@claude-code-plugins"
116+
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ needs.gate.outputs.number }}"
117+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
118+
# or https://code.claude.com/docs/en/cli-reference for available options
119+
120+
- name: Report review result
121+
if: always()
122+
env:
123+
OUTCOME: ${{ steps.review.outcome }}
124+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
125+
with:
126+
script: |
127+
const ok = process.env.OUTCOME === 'success';
128+
await github.rest.repos.createCommitStatus({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
sha: context.payload.workflow_run.head_sha,
132+
context: 'Claude Code Review',
133+
state: ok ? 'success' : 'failure',
134+
description: ok ? 'Review complete' : 'Review failed',
135+
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
136+
});
137+
if (!ok) core.setFailed('Claude Code Review failed');
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Claude Code Review
2+
3+
# Stage 1 (unprivileged): runs on every PR, including forks. Fork PRs get a
4+
# read-only token and no secrets, so the review itself cannot run here. This
5+
# job only records the PR number; the privileged "Claude Code Review (run)"
6+
# workflow picks it up via workflow_run and performs the actual review.
7+
on:
8+
pull_request:
9+
types: [opened, synchronize, ready_for_review, reopened]
10+
11+
concurrency:
12+
group: claude-review-${{ github.event.pull_request.number }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
prepare:
17+
# Skip draft PRs
18+
if: github.event.pull_request.draft == false
19+
runs-on: ubuntu-latest
20+
permissions: {}
21+
steps:
22+
- name: Save PR number
23+
env:
24+
PR_NUMBER: ${{ github.event.pull_request.number }}
25+
run: echo "$PR_NUMBER" > pr-number.txt
26+
27+
- name: Upload PR number
28+
uses: actions/upload-artifact@v4
29+
with:
30+
name: pr-number
31+
path: pr-number.txt
32+
retention-days: 1

.github/workflows/claude.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Claude Code
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
pull_request_review_comment:
7+
types: [created]
8+
issues:
9+
types: [opened, assigned]
10+
pull_request_review:
11+
types: [submitted]
12+
13+
jobs:
14+
claude:
15+
if: |
16+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
pull-requests: read
24+
issues: read
25+
id-token: write
26+
actions: read # Required for Claude to read CI results on PRs
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 1
32+
33+
- name: Run Claude Code
34+
id: claude
35+
uses: anthropics/claude-code-action@v1
36+
with:
37+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38+
39+
# This is an optional setting that allows Claude to read CI results on PRs
40+
additional_permissions: |
41+
actions: read
42+
43+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44+
# prompt: 'Update the pull request description to include a summary of changes.'
45+
46+
# Optional: Add claude_args to customize behavior and configuration
47+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48+
# or https://code.claude.com/docs/en/cli-reference for available options
49+
# claude_args: '--allowed-tools Bash(gh pr *)'
50+

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ testdata/**/*
1818
!assets/js/**/*.yaml
1919
!package*.json
2020
!evcc.dist.yaml
21+
!.coderabbit.yaml
2122
!tests/**/*.evcc.yaml
2223
!tests/**/*.tpl.yaml
2324
!tsconfig.json
@@ -49,3 +50,5 @@ asset-stats.html
4950
/evcc.db
5051
/tsconfig.tsbuildinfo
5152
*storybook.log
53+
/graphify-out/
54+
.graphifyignore

AGENTS.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,10 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w
9090

9191
- No em dashes (—) in comments, commit messages, or docs. Use periods, commas, or colons
9292
- Project name is `evcc`, always lowercase
93+
- In user-facing strings, only mention `evcc` when needed to understand the context. Inside evcc's own UI the self-reference is usually redundant
9394
- Acronyms uppercase in prose: OCPP, MQTT, HEMS, SoC
9495
- Terminology: German "Phasensaldierung" (meter netting signed power across phases each instant) is "summative energy measurement" in English. Avoid "phase balancing" (means load balancing) and "net metering" (a billing scheme)
96+
- Terminology: the top-level load management circuit is "root circuit" in English, "Hauptstromkreis" in German
9597
- Commit subjects: `Component: short description`, no trailing period. Sub-scope in parens: `Meter (Home Assistant): ...`. Use `chore:`/`fix:`/`docs:` only for non-feature changes
9698

9799
## Comment Style
@@ -175,6 +177,7 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w
175177

176178
### State Management
177179

180+
- Never access the store from sub-components; keep them stateless and pass the values they need as props (emit events back to the parent). Only top-level views read from the store. This keeps components reusable and testable (e.g. Storybook should never mock the store).
178181
- Use `reactive()` from Vue for simple global state
179182
- Implement property setters for nested object updates using helper functions
180183
- Use localStorage with reactive wrappers for persistent settings
@@ -255,7 +258,9 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w
255258
- Use `expectModalVisible()` and `expectModalHidden()` helpers
256259
- Test configuration persistence across application restarts
257260
- Standard structure: import `{ start, stop, baseUrl }` from `./evcc`, use `test.afterEach(stop)`
258-
- Never use fixed timeouts, use existance of elements or wait for network idle
261+
- Never use fixed timeouts. Wait on element state (visibility, count, value) instead.
262+
- Never use `page.waitForLoadState("networkidle")`. SPAs keep emitting requests (websockets, polling), so it either races or hangs. Wait for the specific element / value you need instead.
263+
- Keep test names and describe titles short and concrete. They should complement each other, not repeat. Prefer `describe("aux meter") test("create")` over `describe("aux meter") test("create aux meter and verify it appears")`. Drop scenario filler like "and lands in section", "appears correctly", "ensure".
259264

260265
## Device Integration & Configuration
261266

@@ -264,6 +269,7 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w
264269
- Device types: chargers, meters, vehicles, tariffs
265270
- Plugin protocols: Modbus, HTTP, MQTT, JavaScript, Go
266271
- Define device capabilities and configuration in templates at `templates/definition/[type]/`
272+
- Don't restate param properties that `util/templates/defaults.yaml` already defines for that param name. Properties (description, help, type, unit, default, example, required, advanced, mask, private, usages, …) are inherited from defaults; only specify a property in a template to give it a *different* value. Restating the same value is redundant duplication: reference the param by `name` alone.
267273
- Test templates: `evcc --template-type [type] --template [file]`
268274
- Update docs after template changes: `make docs`
269275
- When implementing or debugging against a third-party device library (eebus-go/ship/spine-go, ocpp-go, modbus/SunSpec), consult the library's current upstream documentation before coding rather than relying on recalled API details
@@ -307,3 +313,5 @@ Structure PR descriptions in this order. No headlines. Be concise.
307313
```
308314

309315
Avoid file paths, line numbers, or code listings reproduced from the diff. Include a code snippet only when it conveys the contract (event shape, API signature) more clearly than prose. No testing checklists, no co-author footers, no generator footers.
316+
317+
Never state that `go build`, `go vet`, `go test -race`, or `gofmt` pass (or any "all checks/tests green" phrasing). These are non-negotiable givens that must already be fulfilled, not noteworthy results.

api/feature.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ type Feature int
44

55
//go:generate go tool enumer -type Feature -text
66
const (
7-
_ Feature = iota
8-
CoarseCurrent // charger
9-
IntegratedDevice // charger - always connected - no vehicle, no charging sessions
10-
SwitchDevice // charger - no current control - heat pumps or switch sockets
11-
Heating // charger - heating device - soc ist temperature (°C)
12-
Continuous // charger - heating device where disabled means "normal operation"
13-
Average // tariff
14-
Cacheable // tariff
15-
Offline // vehicle
16-
Retryable // vehicle
17-
Streaming // vehicle
18-
WelcomeCharge // vehicle
19-
ClimaterDisabled // vehicle - ignore climater state for charge control
7+
_ Feature = iota
8+
CoarseCurrent // charger
9+
IntegratedDevice // charger - always connected - no vehicle, no charging sessions
10+
SwitchDevice // charger - no current control - heat pumps or switch sockets
11+
Heating // charger - heating device - soc ist temperature (°C)
12+
Continuous // charger - heating device where disabled means "normal operation"
13+
Average // tariff
14+
Cacheable // tariff
15+
Offline // vehicle
16+
Retryable // vehicle
17+
Streaming // vehicle
18+
WelcomeCharge // vehicle
19+
ClimaterDisabled // vehicle - ignore climater state for charge control
20+
AutodetectDisabled // vehicle - do not try to identify vehicle by status
2021
)

api/feature_enumer.go

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/app.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@
5454
--evcc-accent2: var(--evcc-darker-green);
5555
--evcc-accent3: var(--evcc-darkest-green);
5656

57+
/* heating temperature bar: thermometer scale (cold -> hot) */
58+
--evcc-temp-cold: #1b6fba;
59+
--evcc-temp-warm: #ffa700;
60+
--evcc-temp-hot: #e30022;
61+
--evcc-heating-gradient: linear-gradient(
62+
90deg,
63+
var(--evcc-temp-cold) 5%,
64+
var(--evcc-temp-warm) 20%,
65+
var(--evcc-temp-warm) 40%,
66+
var(--evcc-temp-hot) 95%
67+
);
5768
--evcc-transition-slow: 1000ms;
5869
--evcc-transition-medium: 500ms;
5970
--evcc-transition-fast: 250ms;

0 commit comments

Comments
 (0)