This file provides guidance to AI coding agents when working with code in this repository.
- evcc is an extensible EV Charge Controller and home energy management system written in Go with a Vue.js frontend
- The system manages electric vehicle charging, integrates with solar systems, and provides local energy management without cloud dependencies
- Architecture follows a plugin-based approach for device integrations
make- build full application (UI + Go binary)make build- build Go binary onlymake ui- build UI assets onlymake install- install Go tools and dependenciesmake install-ui- install Node.js dependencies (npm ci)make test- run Go testsmake test-ui- run frontend testsmake lint- run Go linting (golangci-lint)make lint-ui- run frontend lintingnpm run dev- start Vue dev server (http://127.0.0.1:7071)npm run playwright- run integration testsevcc --template-type [type] --template [file]- test device templatesmake docs- generate template documentation
- main.go serves as entry point and embeds web assets and i18n files
- cmd/ contains CLI commands, application setup, and various utility commands (configure, detect, migrate, etc.)
- core/ contains core business logic with main files (loadpoint.go, site.go) and subdirectories:
- loadpoint/ - EV charging point management modules
- planner/ - Smart charging planning algorithms
- coordinator/ - Multi-loadpoint coordination logic
- session/ - Charging session management
- vehicle/ - Vehicle-specific core logic
- soc/ - State of charge handling
- api/ contains API definitions and types
- server/ handles HTTP server, WebSocket, MQTT, database operations, and various handlers
- charger/, meter/, vehicle/ contain device integrations
- tariff/ contains tariff integrations
- plugin/ implements plugin system for device and tariff communication
- assets/ contains Vue.js frontend application
- assets/js/ contains the main TypeScript/Vue.js application with:
- views/ - Vue page components (App.vue, Config.vue, Sessions.vue, etc.)
- components/ - Reusable Vue components
- composables/ - Vue utility functions
- types/ - TypeScript type definitions
- utils/ - Utility functions
- mixins/ - Vue mixins
- assets/css/ contains application stylesheets
- assets/public/ contains static assets and metadata
- i18n/ contains internationalization files
- tests/ contains Playwright integration tests and test configuration files
- dist/ contains built frontend assets (generated)
- Follow Go idioms and conventions (Effective Go)
- Use
gofmtfor formatting, self-documenting names, early returns - Handle all errors explicitly with meaningful messages
- Use interfaces for behavior contracts (small, focused, single responsibility)
- Use
context.Contextfor I/O, long-running, or cancelable operations - Organize code into logical packages with clear responsibilities
- Prefer composition over inheritance, minimize external dependencies
_blueprint.go- templates for new device implementations_enumer.go- generated enum code*_decorators.go- generated decorator pattern implementations- Validate interface implementations:
var _ Interface = (*Type)(nil)
- Wrap errors with context:
fmt.Errorf("context: %w", err) - Use
errors.Asanderrors.Isfor type checking - Use
errors.Joinfor combining errors (prefer customjoinErrorshelper) - Create domain-specific error types (ClassError, DeviceError)
- Use
backoff.Permanent(err)for non-retryable errors - Implement panic recovery with
deferandrecover()in script contexts
- Use
testingpackage withtestify/assertandtestify/require - Table-driven tests with struct definitions for multiple cases
- Use
gomockfor interface mocking,go:generate mockgenfor generation - Test both success and failure scenarios, use
requirefor setup,assertfor tests - Use
go:generatefor code generation, regenerate after interface/enum changes - Never manually edit generated files
- Use
context.Contextas first parameter for I/O operations - Use
context.WithTimeout,context.WithCancelappropriately - Check
ctx.Done()in long-running loops - Propagate context through goroutines for proper cancellation
- Handle concurrent operations safely with Go's concurrency primitives
- Filter
NaNandInfinityvalues usingmath.IsNaN()andmath.IsInf() - Validate numeric inputs from external sources
- Use helper functions like
parseFloat()that reject invalid values
- Use Vue 3 Options API (preferred over Composition API)
- Use reactive stores without Vuex/Pinia for cross-component state
- Use global app instance (
window.app) only for: notifications (raise()), offline status (setOffline()/setOnline()), clearing notifications (clear()) - Organize components by feature/domain in
assets/js/components/subdirectories
- Use TypeScript for all new frontend code
- Use
constinstead offunctionfor component methods (e.g.,const updateType = () =>) - Define TypeScript interfaces for component props, data, and API responses
- Implement accessibility features (tabindex, aria-label, keyboard handlers)
- Use descriptive names for variables, functions, and event handlers
- Use early returns for readability
- Use configured Axios instance for HTTP communication
- Use
reactive()from Vue for simple global state - Implement property setters for nested object updates using helper functions
- Use localStorage with reactive wrappers for persistent settings
- Use Vue
watch()for automatic persistence of settings changes - Separate concerns with dedicated stores (settings, application state)
- Define comprehensive interfaces for API responses and application state
- Use enums for constants (e.g.,
THEME,CURRENCY) - Extend global interfaces for window object augmentation
- Use union types for flexible but type-safe configurations
- Use generic types for reusable utility functions
- Handle type assertions carefully with proper error handling
- Create focused utility functions with proper TypeScript typing
- Use CSS Custom Properties for theming (semantic names:
--evcc-green,--evcc-battery) - Use existing custom media queries for responsive breakpoints
- Use
$t()function for all user-facing strings - Update both
i18n/en.jsonandi18n/de.jsonfor new strings - Use hierarchical namespace:
{section}.{component}.{purpose} - Examples:
config.vehicle.titleAdd,main.vehicleStatus.charging - Action patterns:
titleAdd,titleEdit,save,cancel,delete,validateSave - Use placeholders for dynamic content:
{soc},{duration},{value} - Prefer context-specific keys over generic ones
- Test with German translations (20-40% longer text)
- Write integration tests using Playwright for user workflows
- Use Storybook for component development and visual testing
- Use semantic selectors (roles, labels, button text);
data-testidonly when necessary - Test error states and loading states
- Location:
tests/directory with.spec.tsfiles - Configuration:
.evcc.yamlfiles for different test scenarios - Utilities:
tests/utils.tsfor common helpers,tests/evcc.tsfor binary management - Categories:
config-*.spec.ts(UI config),sessions.spec.ts/plan.spec.ts(workflows),smart-cost.spec.ts/limits.spec.ts(features),backup-restore.spec.ts/auth.spec.ts(integration)
- Base URL:
http://127.0.0.1:7070 - Parallel execution with different ports per worker for isolation
- Uses
./evccbinary with test-specific configuration files - Each worker uses isolated temporary database files
- Always runs with English UI language
- Must build before testing executing playwright
make ui buildsince it uses the binary. For manual testing assets are build and reloaded automatically (vite dev). - Run tests:
npm run playwrightornpx playwright test - Debug:
npx playwright test --debug - Specific test:
npx playwright test tests/config-loadpoint.spec.ts
- Preferred: Semantic selectors using
getByRole(),getByLabel(),getByText() - Fallback:
data-testidonly when semantic selectors aren't available - Examples:
page.getByRole("button", { name: "Add charger" })page.getByLabel("Manufacturer").selectOption("Demo charger")page.getByRole("listitem", { name: "Draggable: First Loadpoint" })(using aria-label)page.getByTestId("loadpoint")(fallback only)
- never use
.locator()orclassandid-based selectors
- Use test-specific
.evcc.yamlconfigurations - Import utilities from
tests/utils.tsfor common operations - Focus on complete user journeys rather than isolated interactions
- Use
expectModalVisible()andexpectModalHidden()helpers - Test configuration persistence across application restarts
- Standard structure: import
{ start, stop, baseUrl }from./evcc, usetest.afterEach(stop) - Never use fixed timeouts, use existance of elements or wait for network idle
- Device types: chargers, meters, vehicles, tariffs
- Plugin protocols: Modbus, HTTP, MQTT, JavaScript, Go
- Define device capabilities and configuration in templates at
templates/definition/[type]/ - Test templates:
evcc --template-type [type] --template [file] - Update docs after template changes:
make docs
- Use YAML format for all configuration files (default:
evcc.yaml, or specify with--config) - Provide clear validation and error messages for invalid configurations
- Support template-based device configurations with meaningful defaults
- Use SQLite as default database (default:
evcc.db, or specify with--database) with proper migrations and data integrity
- Validate all user inputs and sanitize data before database storage
- Use secure protocols (TLS) for external integrations
- Implement proper authentication and authorization
- Never log sensitive information (passwords, tokens, personal data)
- Optimize database queries with appropriate indexes
- Handle concurrent operations safely with Go's concurrency primitives
- Implement proper caching strategies and connection pooling
- Avoid blocking operations in main application loop
- Include appropriate comments for complex business logic