├── .cursorrules
├── .devcontainer
└── devcontainer.json
├── .dockerignore
├── .editorconfig
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ ├── config.yml
│ └── feature_request.md
├── copilot-instructions.md
├── dependabot.yml
├── issue_label_bot.yaml
└── workflows
│ ├── _claude-issue-triage.yml_
│ ├── codeql.yml
│ ├── default.yml
│ ├── docs-issue.yml
│ ├── documentation.yml
│ ├── language-reminder.yml
│ ├── nightly.yml
│ ├── openapi-validate.yml
│ ├── release.yml
│ ├── schema.yml
│ ├── stale.yaml
│ └── website.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser-nightly.yml
├── .goreleaser.yml
├── .prettierignore
├── .storybook
├── main.ts
└── preview.ts
├── .vscode
└── extensions.json
├── AGENTS.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── LICENSES
├── dependencies.md
├── exclusions.md
├── fonts.md
└── icons.md
├── Makefile
├── README.md
├── api
├── actionconfig.go
├── actionconfig_test.go
├── api.go
├── batterymode.go
├── batterymode_enumer.go
├── chargemode.go
├── chargemodestatus.go
├── error.go
├── feature.go
├── feature_enumer.go
├── globalconfig
│ └── types.go
├── marshal.go
├── mock.go
├── plans.go
├── plugin.go
├── proto
│ ├── auth.proto
│ ├── pb
│ │ ├── auth.pb.go
│ │ ├── auth_grpc.pb.go
│ │ ├── vehicle.pb.go
│ │ ├── vehicle_grpc.pb.go
│ │ ├── victron.pb.go
│ │ └── victron_grpc.pb.go
│ ├── vehicle.proto
│ └── victron.proto
├── rates.go
├── rates_test.go
├── reason.go
├── reason_enumer.go
├── store
│ └── types.go
├── tariff.go
├── tarifftype_enumer.go
└── tariffusage_enumer.go
├── assets
├── css
│ └── app.css
├── font
│ ├── Montserrat-Bold.woff2
│ └── Montserrat-Medium.woff2
├── github
│ ├── evcc-gopher.png
│ └── screenshot.webp
├── index.html
├── js
│ ├── api.ts
│ ├── app.ts
│ ├── colors.ts
│ ├── components
│ │ ├── Auth
│ │ │ ├── LoginModal.vue
│ │ │ ├── PasswordInput.vue
│ │ │ ├── PasswordModal.vue
│ │ │ └── auth.ts
│ │ ├── Battery
│ │ │ └── BatterySettingsModal.vue
│ │ ├── ChargingPlans
│ │ │ ├── Arrival.vue
│ │ │ ├── ChargingPlan.stories.ts
│ │ │ ├── ChargingPlan.vue
│ │ │ ├── PlanRepeatingSettings.vue
│ │ │ ├── PlanStaticSettings.vue
│ │ │ ├── PlansRepeatingSettings.vue
│ │ │ ├── PlansSettings.vue
│ │ │ ├── PreconditionSelect.vue
│ │ │ ├── Preview.stories.ts
│ │ │ ├── Preview.test.ts
│ │ │ ├── Preview.vue
│ │ │ ├── Warnings.vue
│ │ │ └── types.d.ts
│ │ ├── Config
│ │ │ ├── BackupRestoreModal.vue
│ │ │ ├── ChargerModal.vue
│ │ │ ├── CircuitsModal.vue
│ │ │ ├── ControlModal.vue
│ │ │ ├── DeviceCard.vue
│ │ │ ├── DeviceModal
│ │ │ │ ├── Actions.vue
│ │ │ │ ├── DeviceModalBase.vue
│ │ │ │ ├── Modbus.vue
│ │ │ │ ├── SponsorTokenRequired.vue
│ │ │ │ ├── TemplateSelector.vue
│ │ │ │ ├── YamlEntry.vue
│ │ │ │ └── index.ts
│ │ │ ├── DeviceTags.vue
│ │ │ ├── EebusModal.vue
│ │ │ ├── ExperimentalBanner.vue
│ │ │ ├── FormRow.vue
│ │ │ ├── GeneralConfig.vue
│ │ │ ├── GeneralConfigEntry.vue
│ │ │ ├── HemsModal.vue
│ │ │ ├── InfluxModal.vue
│ │ │ ├── JsonModal.vue
│ │ │ ├── LoadpointModal.vue
│ │ │ ├── Markdown.vue
│ │ │ ├── MessagingModal.vue
│ │ │ ├── MeterCard.vue
│ │ │ ├── MeterModal.vue
│ │ │ ├── ModbusProxyModal.vue
│ │ │ ├── MqttModal.vue
│ │ │ ├── NetworkModal.vue
│ │ │ ├── NewDeviceButton.vue
│ │ │ ├── PropertyCertField.vue
│ │ │ ├── PropertyCollapsible.vue
│ │ │ ├── PropertyEntry.vue
│ │ │ ├── PropertyField.vue
│ │ │ ├── PropertyFileField.vue
│ │ │ ├── ShmModal.vue
│ │ │ ├── SponsorModal.vue
│ │ │ ├── TariffsModal.vue
│ │ │ ├── TelemetryModal.vue
│ │ │ ├── TestResult.vue
│ │ │ ├── TitleModal.vue
│ │ │ ├── VehicleModal.vue
│ │ │ ├── WelcomeBanner.vue
│ │ │ ├── YamlEditor.vue
│ │ │ ├── YamlEditorContainer.vue
│ │ │ ├── YamlModal.vue
│ │ │ ├── defaultYaml
│ │ │ │ ├── circuits.yaml
│ │ │ │ ├── customCharger.yaml
│ │ │ │ ├── customHeater.yaml
│ │ │ │ ├── eebus.yaml
│ │ │ │ ├── heatpump.yaml
│ │ │ │ ├── hems.yaml
│ │ │ │ ├── messaging.yaml
│ │ │ │ ├── meter.yaml
│ │ │ │ ├── modbusproxy.yaml
│ │ │ │ ├── sgready.yaml
│ │ │ │ ├── sgreadyRelay.yaml
│ │ │ │ ├── switchsocketCharger.yaml
│ │ │ │ ├── switchsocketHeater.yaml
│ │ │ │ ├── tariffs.yaml
│ │ │ │ └── vehicle.yaml
│ │ │ ├── mixins
│ │ │ │ └── test.js
│ │ │ └── utils
│ │ │ │ └── test.ts
│ │ ├── Energyflow
│ │ │ ├── BatteryIcon.stories.ts
│ │ │ ├── BatteryIcon.vue
│ │ │ ├── Energyflow.stories.ts
│ │ │ ├── Energyflow.vue
│ │ │ ├── Entry.vue
│ │ │ ├── LabelBar.vue
│ │ │ └── Visualization.vue
│ │ ├── Footer
│ │ │ ├── Footer.stories.ts
│ │ │ ├── Footer.vue
│ │ │ ├── Logo.vue
│ │ │ ├── OfflineIndicator.stories.ts
│ │ │ ├── OfflineIndicator.vue
│ │ │ ├── RestartButton.vue
│ │ │ ├── Version.stories.ts
│ │ │ └── Version.vue
│ │ ├── Forecast
│ │ │ ├── ActiveSlot.vue
│ │ │ ├── Chart.vue
│ │ │ ├── Details.vue
│ │ │ ├── ForecastModal.vue
│ │ │ ├── TypeSelect.vue
│ │ │ └── types.ts
│ │ ├── GlobalSettings
│ │ │ ├── GlobalSettingsModal.vue
│ │ │ ├── LoadpointOrderSettings.vue
│ │ │ └── UserInterfaceSettings.vue
│ │ ├── HelpModal.vue
│ │ ├── Helper
│ │ │ ├── AnimatedNumber.vue
│ │ │ ├── CopyButton.vue
│ │ │ ├── CustomSelect.vue
│ │ │ ├── DragDropItem.vue
│ │ │ ├── DragDropList.vue
│ │ │ ├── FormRow.vue
│ │ │ ├── GenericModal.vue
│ │ │ ├── IconSelectGroup.vue
│ │ │ ├── IconSelectItem.vue
│ │ │ ├── LabelAndValue.vue
│ │ │ ├── MultiSelect.vue
│ │ │ ├── SelectGroup.story.vue
│ │ │ └── SelectGroup.vue
│ │ ├── HemsWarning.vue
│ │ ├── Issue
│ │ │ ├── AdditionalItem.vue
│ │ │ ├── SummaryModal.vue
│ │ │ ├── format.test.ts
│ │ │ ├── format.ts
│ │ │ ├── template.test.ts
│ │ │ ├── template.ts
│ │ │ └── types.d.ts
│ │ ├── Loadpoints
│ │ │ ├── Loadpoint.stories.ts
│ │ │ ├── Loadpoint.vue
│ │ │ ├── Loadpoints.stories.ts
│ │ │ ├── Loadpoints.vue
│ │ │ ├── Mode.stories.ts
│ │ │ ├── Mode.vue
│ │ │ ├── Phases.stories.ts
│ │ │ ├── Phases.vue
│ │ │ ├── SessionInfo.vue
│ │ │ ├── SettingsBatteryBoost.vue
│ │ │ ├── SettingsButton.vue
│ │ │ └── SettingsModal.vue
│ │ ├── MaterialIcon
│ │ │ ├── Add.vue
│ │ │ ├── BatteryBoost.vue
│ │ │ ├── Circuits.vue
│ │ │ ├── Climater.vue
│ │ │ ├── CloudOffline.vue
│ │ │ ├── Dropdown.vue
│ │ │ ├── DynamicPrice.vue
│ │ │ ├── Edit.vue
│ │ │ ├── Eebus.vue
│ │ │ ├── Forecast.vue
│ │ │ ├── Hems.vue
│ │ │ ├── Influx.vue
│ │ │ ├── Loadpoint.vue
│ │ │ ├── MaterialIcon.story.ts
│ │ │ ├── ModbusProxy.vue
│ │ │ ├── Mqtt.vue
│ │ │ ├── Notification.vue
│ │ │ ├── PlanEnd.vue
│ │ │ ├── PlanStart.vue
│ │ │ ├── Play.vue
│ │ │ ├── Question.vue
│ │ │ ├── Reconnect.vue
│ │ │ ├── Record.vue
│ │ │ ├── Restart.vue
│ │ │ ├── RfidWait.vue
│ │ │ ├── Shm.vue
│ │ │ ├── SunDown.vue
│ │ │ ├── SunPause.vue
│ │ │ ├── SunUp.vue
│ │ │ ├── Sync.vue
│ │ │ ├── TempLimit.vue
│ │ │ ├── Total.vue
│ │ │ ├── VehicleLimit.vue
│ │ │ ├── VehicleLimitReached.vue
│ │ │ ├── VehicleLimitWarning.vue
│ │ │ ├── VehicleMinSoc.vue
│ │ │ └── Welcome.vue
│ │ ├── MultiIcon
│ │ │ ├── 1.vue
│ │ │ ├── 2.vue
│ │ │ ├── 3.vue
│ │ │ ├── 4.vue
│ │ │ ├── 5.vue
│ │ │ ├── 6.vue
│ │ │ ├── 7.vue
│ │ │ ├── 8.vue
│ │ │ ├── 9.vue
│ │ │ ├── MultiIcon.stories.ts
│ │ │ ├── MultiIcon.vue
│ │ │ ├── Plus.vue
│ │ │ └── index.ts
│ │ ├── Optimize
│ │ │ ├── BatteryConfigurationTable.vue
│ │ │ ├── ChargeChart.vue
│ │ │ ├── CopyButton.vue
│ │ │ ├── PriceChart.vue
│ │ │ ├── SocChart.vue
│ │ │ ├── TimeSeriesDataTable.vue
│ │ │ └── compactJson.ts
│ │ ├── Savings
│ │ │ ├── LiveCommunity.stories.ts
│ │ │ ├── LiveCommunity.vue
│ │ │ ├── Savings.vue
│ │ │ ├── Sponsor.stories.ts
│ │ │ ├── Sponsor.vue
│ │ │ ├── SponsorTokenExpires.stories.ts
│ │ │ ├── SponsorTokenExpires.vue
│ │ │ ├── Tile.stories.ts
│ │ │ ├── Tile.vue
│ │ │ ├── co2Reference.ts
│ │ │ ├── communityApi.ts
│ │ │ └── types.d.ts
│ │ ├── Sessions
│ │ │ ├── AvgCostGroupedChart.vue
│ │ │ ├── CostGroupedChart.vue
│ │ │ ├── CostHistoryChart.vue
│ │ │ ├── DateNavigator.vue
│ │ │ ├── DateNavigatorButton.vue
│ │ │ ├── EnergyGroupedChart.vue
│ │ │ ├── EnergyHistoryChart.vue
│ │ │ ├── LegendList.vue
│ │ │ ├── PeriodSelector.vue
│ │ │ ├── SessionDetailsModal.vue
│ │ │ ├── SessionTable.vue
│ │ │ ├── SolarGroupedChart.vue
│ │ │ ├── SolarYearChart.vue
│ │ │ ├── chartConfig.ts
│ │ │ └── types.ts
│ │ ├── Site
│ │ │ ├── Site.vue
│ │ │ ├── WelcomeIcons.vue
│ │ │ └── types.d.ts
│ │ ├── Tariff
│ │ │ ├── SmartCostLimit.vue
│ │ │ ├── SmartFeedInPriority.vue
│ │ │ ├── SmartTariffBase.vue
│ │ │ └── TariffChart.vue
│ │ ├── TelemetrySettings.vue
│ │ ├── Top
│ │ │ ├── Header.vue
│ │ │ ├── Navigation.stories.ts
│ │ │ ├── Navigation.vue
│ │ │ ├── Notifications.stories.ts
│ │ │ ├── Notifications.vue
│ │ │ ├── baseapi.ts
│ │ │ └── types.d.ts
│ │ ├── VehicleIcon
│ │ │ ├── Airpurifier.vue
│ │ │ ├── Battery.vue
│ │ │ ├── Bike.vue
│ │ │ ├── Bulb.vue
│ │ │ ├── Bus.vue
│ │ │ ├── Climate.vue
│ │ │ ├── Coffeemaker.vue
│ │ │ ├── Compute.vue
│ │ │ ├── Cooking.vue
│ │ │ ├── Cooler.vue
│ │ │ ├── Desktop.vue
│ │ │ ├── Device.vue
│ │ │ ├── Dishwasher.vue
│ │ │ ├── Dryer.vue
│ │ │ ├── Floorlamp.vue
│ │ │ ├── Generic.vue
│ │ │ ├── Heater.vue
│ │ │ ├── Heatexchange.vue
│ │ │ ├── Heatpump.vue
│ │ │ ├── Kettle.vue
│ │ │ ├── Laundry.vue
│ │ │ ├── Laundry2.vue
│ │ │ ├── Machine.vue
│ │ │ ├── Meter.vue
│ │ │ ├── Microwave.vue
│ │ │ ├── Moped.vue
│ │ │ ├── Motorcycle.vue
│ │ │ ├── Pump.vue
│ │ │ ├── Rickshaw.vue
│ │ │ ├── Rocket.vue
│ │ │ ├── Scooter.vue
│ │ │ ├── Shuttle.vue
│ │ │ ├── SmartConsumer.vue
│ │ │ ├── Taxi.vue
│ │ │ ├── Tool.vue
│ │ │ ├── Tractor.vue
│ │ │ ├── Van.vue
│ │ │ ├── VehicleIcon.stories.ts
│ │ │ ├── VehicleIcon.vue
│ │ │ ├── WaterHeater.vue
│ │ │ └── index.ts
│ │ └── Vehicles
│ │ │ ├── LimitEnergySelect.vue
│ │ │ ├── LimitSocSelect.vue
│ │ │ ├── Options.vue
│ │ │ ├── Soc.vue
│ │ │ ├── Status.story.vue
│ │ │ ├── Status.test.ts
│ │ │ ├── Status.vue
│ │ │ ├── StatusItem.vue
│ │ │ ├── Title.vue
│ │ │ ├── Vehicle.stories.ts
│ │ │ └── Vehicle.vue
│ ├── experimental.js
│ ├── featureflags.ts
│ ├── i18n.ts
│ ├── mixins
│ │ ├── breakpoint.ts
│ │ ├── collector.ts
│ │ ├── formatter.test.ts
│ │ ├── formatter.ts
│ │ └── icon.ts
│ ├── restart.ts
│ ├── router.ts
│ ├── settings.ts
│ ├── store.ts
│ ├── theme.ts
│ ├── types
│ │ ├── evcc.ts
│ │ ├── shopicons.d.ts
│ │ └── vue.d.ts
│ ├── uiLoadpoints.ts
│ ├── units.ts
│ ├── utils
│ │ ├── cleanYaml.test.ts
│ │ ├── cleanYaml.ts
│ │ ├── convertRates.ts
│ │ ├── debounce.ts
│ │ ├── deepClone.ts
│ │ ├── deepEqual.ts
│ │ ├── energyOptions.ts
│ │ ├── fatal.ts
│ │ ├── forecast.test.ts
│ │ ├── forecast.ts
│ │ ├── log.ts
│ │ ├── native.ts
│ │ ├── sleep.ts
│ │ └── useDebouncedComputed.ts
│ └── views
│ │ ├── App.vue
│ │ ├── Config.vue
│ │ ├── Energy.vue
│ │ ├── Issue.vue
│ │ ├── Log.vue
│ │ ├── Main.vue
│ │ ├── Optimize.vue
│ │ └── Sessions.vue
└── public
│ └── meta
│ ├── android-chrome-192x192-maskable.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512-maskable.png
│ ├── android-chrome-512x512.png
│ ├── android-chrome-maskable.svg
│ ├── android-chrome-monochrome.svg
│ ├── android-chrome.svg
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── mstile-144x144.png
│ ├── mstile-150x150.png
│ ├── mstile-310x150.png
│ ├── mstile-310x310.png
│ ├── mstile-70x70.png
│ ├── safari-pinned-tab.svg
│ └── site.webmanifest
├── charger
├── _blueprint.go
├── abb.go
├── abl-em4.go
├── abl.go
├── abl_decorators.go
├── alfen.go
├── alfen_decorators.go
├── alphatec.go
├── alpitronic.go
├── amperfied.go
├── amperfied_decorators.go
├── bender.go
├── bender_decorators.go
├── cfos.go
├── cfos_decorators.go
├── charger.go
├── charger_decorators.go
├── compleo.go
├── config.go
├── config
│ └── config.go
├── connectiq.go
├── connectiq
│ └── types.go
├── dadapower.go
├── daheimladen.go
├── daheimladen_decorators.go
├── delta.go
├── easee.go
├── easee
│ ├── identity.go
│ ├── log.go
│ ├── observationid_enumer.go
│ ├── signalr.go
│ └── types.go
├── easee_test.go
├── echarge
│ ├── ecb1
│ │ └── types.go
│ ├── salia
│ │ ├── types.go
│ │ └── types_test.go
│ └── types.go
├── eebus.go
├── eebus_decorators.go
├── eebus_test.go
├── em2go-duo.go
├── em2go.go
├── em2go_decorators.go
├── embed.go
├── eprowallbox.go
├── etrel.go
├── evecube.go
├── evecube_decorators.go
├── evse
│ └── types.go
├── evsedin.go
├── evsedin_decorators.go
├── evsewifi.go
├── evsewifi_decorators.go
├── evsewifi_test.go
├── fritzdect.go
├── fronius-wattpilot.go
├── go-e.go
├── go-e
│ ├── api.go
│ ├── api_test.go
│ ├── types.go
│ └── types2.go
├── go-e_decorators.go
├── go-e_test.go
├── hardybarth-ecb1.go
├── hardybarth-salia.go
├── hardybarth-salia_decorators.go
├── heatpump.go
├── heatpump_decorators.go
├── heidelberg-ec.go
├── helper.go
├── hesotec.go
├── homeassistant-switch.go
├── homeassistant.go
├── homeassistant_decorators.go
├── homematic.go
├── homewizard.go
├── innogy.go
├── innogy_decorators.go
├── kathrein.go
├── keba-modbus.go
├── keba-modbus_decorators.go
├── keba-udp.go
├── keba-udp_decorators.go
├── keba
│ ├── listener.go
│ ├── sender.go
│ └── types.go
├── kse.go
├── kse_decorators.go
├── measurement
│ ├── energy.go
│ └── heating.go
├── mennekes-compact.go
├── mennekes-compact_decorators.go
├── mennekes-hcc3.go
├── mypv.go
├── mystrom.go
├── nrg
│ ├── ble
│ │ ├── nrg_linux.go
│ │ └── types.go
│ └── connect
│ │ └── types.go
├── nrgble.go
├── nrgble_linux.go
├── nrgconnect.go
├── nrggen2.go
├── nrggen2_decorators.go
├── obo.go
├── ocpp.go
├── ocpp
│ ├── connector.go
│ ├── connector_core.go
│ ├── connector_requests.go
│ ├── connector_test.go
│ ├── const.go
│ ├── cp.go
│ ├── cp_core.go
│ ├── cp_requests.go
│ ├── cp_setup.go
│ ├── cs.go
│ ├── cs_core.go
│ ├── cs_log.go
│ ├── helper.go
│ ├── helper_test.go
│ └── instance.go
├── ocpp_decorators.go
├── ocpp_test.go
├── ocpp_test_handler.go
├── ocpp_test_logger.go
├── openevse.go
├── openevse
│ └── types.go
├── openevse_decorators.go
├── openwb-2.0.go
├── openwb-2.0_decorators.go
├── openwb-pro.go
├── openwb-pro_decorators.go
├── openwb.go
├── openwb
│ ├── pro
│ │ └── types.go
│ └── topics.go
├── openwb_decorators.go
├── pantabox.go
├── pcelectric.go
├── pcelectric
│ └── types.go
├── pcelectric_decorators.go
├── peblar.go
├── peblar_decorators.go
├── phoenix-charx.go
├── phoenix-charx_decorators.go
├── phoenix-em-eth.go
├── phoenix-em-eth_decorators.go
├── phoenix-ev-eth.go
├── phoenix-ev-eth_decorators.go
├── phoenix-ev-ser.go
├── plugchoice.go
├── plugchoice
│ ├── api.go
│ └── types.go
├── pracht-alpha.go
├── pulsares.go
├── pulsares_decorators.go
├── pulsatrix.go
├── schneider-v3.go
├── semp.go
├── semp
│ ├── connection.go
│ └── types.go
├── semp_decorators.go
├── semp_test.go
├── sgready-relay.go
├── sgready.go
├── sgready_decorators.go
├── shelly.go
├── shelly_decorators.go
├── sigenergy.go
├── smaevcharger.go
├── smaevcharger
│ ├── identity.go
│ └── types.go
├── smartevse.go
├── solax.go
├── sungrow.go
├── switchsocket.go
├── switchsocket_decorators.go
├── tapo.go
├── tasmota.go
├── tasmota_decorators.go
├── template.go
├── template_test.go
├── tessie.go
├── tplink.go
├── trydan.go
├── twc3.go
├── vaillant.go
├── vaillant_decorators.go
├── vehicle-api.go
├── versicharge.go
├── vestel.go
├── vestel_decorators.go
├── victron.go
├── wallbe.go
├── wallbe_decorators.go
├── warp
│ ├── const.go
│ ├── externalcontrol_enumer.go
│ └── types.go
├── warp2.go
├── warp2_decorators.go
├── webasto-next.go
├── weidmüller.go
├── weidmüller_decorators.go
├── zaptec.go
├── zaptec
│ ├── const.go
│ ├── observationid_enumer.go
│ └── types.go
└── zaptec_decorators.go
├── cmd
├── cache-clear.go
├── cache-get.go
├── cache.go
├── charger.go
├── charger_ramp.go
├── check_config.go
├── class_enumer.go
├── config.go
├── config_delete.go
├── configure.go
├── configure
│ ├── configure.go
│ ├── configure.tpl
│ ├── devicetest.go
│ ├── eebus.go
│ ├── flow.go
│ ├── helper.go
│ ├── localization
│ │ ├── de.toml
│ │ └── en.toml
│ ├── main.go
│ ├── survey.go
│ ├── texts.go
│ └── types.go
├── decorate
│ ├── decorate.go
│ ├── decorate.tpl
│ ├── decorate_decorators.go
│ └── decorate_test.go
├── demo.go
├── demo.yaml
├── detect.go
├── detect
│ ├── analyze.go
│ ├── definitions.go
│ ├── tasklist.go
│ ├── tasks
│ │ ├── const.go
│ │ ├── http.go
│ │ ├── keba.go
│ │ ├── modbus.go
│ │ ├── mqtt.go
│ │ ├── ping.go
│ │ ├── registry.go
│ │ ├── sma.go
│ │ ├── tcp.go
│ │ └── types.go
│ └── work.go
├── device.go
├── discuss.go
├── discuss.tpl
├── dump.go
├── dump.tpl
├── dumper.go
├── eebus.go
├── error.go
├── error_test.go
├── flags.go
├── gendock.go
├── health.go
├── helper.go
├── meter.go
├── migrate.go
├── ocpp
│ ├── handler.go
│ └── main.go
├── openapi
│ └── openapi.go
├── password.go
├── password_reset.go
├── password_set.go
├── password_test.go
├── refs.go
├── root.go
├── root_test.go
├── settings-get.go
├── settings-set.go
├── settings.go
├── setup.go
├── setup_circuits_test.go
├── setup_test.go
├── shutdown
│ └── shutdown.go
├── soc
│ └── main.go
├── sponsor.go
├── sunspec.go
├── tariff.go
├── token.go
├── token_ford-connect.go
├── token_psa.go
├── token_tronity.go
└── vehicle.go
├── core
├── circuit
│ ├── circuit.go
│ ├── circuit_test.go
│ └── config.go
├── coordinator
│ ├── adapter.go
│ ├── api.go
│ ├── coordinator.go
│ ├── coordinator_test.go
│ └── dummy.go
├── energy_metrics.go
├── energy_metrics_test.go
├── health.go
├── helper.go
├── keys
│ ├── auth.go
│ ├── global.go
│ ├── loadpoint.go
│ └── site.go
├── loadpoint.go
├── loadpoint
│ ├── api.go
│ ├── config.go
│ ├── error.go
│ ├── mock.go
│ ├── pollmode_enumer.go
│ └── types.go
├── loadpoint_api.go
├── loadpoint_charger.go
├── loadpoint_effective.go
├── loadpoint_effective_test.go
├── loadpoint_mutex.go
├── loadpoint_phases.go
├── loadpoint_phases_test.go
├── loadpoint_plan.go
├── loadpoint_session.go
├── loadpoint_session_test.go
├── loadpoint_smartcost.go
├── loadpoint_status_test.go
├── loadpoint_sync_test.go
├── loadpoint_test.go
├── loadpoint_vehicle.go
├── loadpoint_vehicle_test.go
├── meterenergy.go
├── meterenergy_test.go
├── metrics
│ ├── db.go
│ └── types.go
├── optimizer.md
├── planner
│ ├── helper.go
│ ├── helper_test.go
│ ├── planner.go
│ ├── planner.md
│ ├── planner.svg
│ ├── planner_test.go
│ ├── sort.go
│ └── sort_test.go
├── prioritizer
│ ├── prioritizer.go
│ └── prioritizer_test.go
├── progress.go
├── progress_test.go
├── session
│ ├── db.go
│ ├── format_test.go
│ └── session.go
├── settings
│ ├── config.go
│ ├── database.go
│ └── settings.go
├── site.go
├── site
│ ├── api.go
│ └── vehicles.go
├── site_api.go
├── site_battery.go
├── site_battery_test.go
├── site_circuit_test.go
├── site_circuits.go
├── site_optimizer.go
├── site_optimizer_test.go
├── site_tariffs.go
├── site_test.go
├── site_vehicles.go
├── soc
│ ├── estimator.go
│ ├── estimator_test.go
│ └── helper.go
├── solar.go
├── solar_test.go
├── stats.go
├── timer.go
├── timer_test.go
├── vehicle
│ ├── adapter.go
│ ├── api.go
│ ├── dummy.go
│ ├── mock.go
│ └── vehicle.go
└── wrapper
│ ├── chargemeter.go
│ ├── chargemeter_test.go
│ ├── chargerater.go
│ ├── chargerater_test.go
│ ├── chargetimer.go
│ └── chargetimer_test.go
├── env.d.ts
├── eslint.config.mts
├── evcc.dist.yaml
├── go.mod
├── go.sum
├── hems
├── config.go
├── eebus
│ ├── eebus.go
│ ├── events.go
│ └── types.go
├── relay
│ └── relay.go
├── shared
│ └── helper.go
└── shm
│ ├── messages.go
│ └── shm.go
├── i18n
├── .prettierrc
├── ar.json
├── bg.json
├── ca.json
├── check.ts
├── cs.json
├── da.json
├── de.json
├── el.json
├── en.json
├── es.json
├── et.json
├── fi.json
├── fr.json
├── hr.json
├── hu.json
├── it.json
├── lb.json
├── lt.json
├── nl.json
├── no.json
├── pl.json
├── pt.json
├── ro.json
├── ru.json
├── sk.json
├── sl.json
├── sv.json
├── ta.json
├── tr.json
├── uk.json
└── zh-Hans.json
├── icon.png
├── jest.config.ts
├── lm.md
├── main.go
├── meter
├── _blueprint.go
├── bosch
│ ├── api.go
│ └── types.go
├── bosch_bpts5_hybrid.go
├── bosch_bpts5_hybrid_decorators.go
├── cfos.go
├── config.go
├── config
│ └── config.go
├── discovergy.go
├── discovergy
│ └── types.go
├── dsmr.go
├── dsmr_decorators.go
├── e3dc.go
├── e3dc_decorators.go
├── eebus.go
├── eebus_test.go
├── fritzdect.go
├── fritzdect
│ └── fritzdect.go
├── goodwe-wifi.go
├── goodwe-wifi_decorators.go
├── goodwe
│ ├── server.go
│ └── types.go
├── homeassistant.go
├── homeassistant_decorators.go
├── homematic.go
├── homematic
│ ├── connection.go
│ ├── types.go
│ └── types_test.go
├── homewizard.go
├── homewizard
│ ├── connection.go
│ ├── types.go
│ └── types_test.go
├── lgess.go
├── lgess_decorators.go
├── lgpcs
│ ├── lgpcs.go
│ └── types.go
├── mbmd.go
├── mbmd_decorators.go
├── mbmd_operation.go
├── measurement
│ ├── energy.go
│ └── phases.go
├── meter.go
├── meter_average.go
├── meter_decorators.go
├── meter_test.go
├── mystrom.go
├── mystrom
│ └── mystrom.go
├── obis
│ └── obis.go
├── openwb.go
├── powerwall.go
├── powerwall_decorators.go
├── rct.go
├── rct_decorators.go
├── shelly.go
├── shelly
│ ├── connection.go
│ ├── gen1.go
│ ├── gen1_test.go
│ ├── gen2.go
│ ├── gen2_test.go
│ ├── types.go
│ └── types_test.go
├── shelly_decorators.go
├── sma.go
├── sma_decorators.go
├── tapo.go
├── tapo
│ └── connection.go
├── tasmota.go
├── tasmota
│ ├── connection.go
│ ├── types.go
│ └── types_test.go
├── tasmota_decorators.go
├── template.go
├── template_test.go
├── tibber-pulse.go
├── tibber
│ ├── client.go
│ └── types.go
├── tplink.go
├── tplink
│ ├── connection.go
│ ├── types.go
│ └── types_test.go
├── tq-em.go
├── tq-em420.go
├── tq-em_decorators.go
├── usage_battery.go
├── usage_pv.go
├── zendure.go
├── zendure
│ ├── connection.go
│ ├── connection_test.go
│ ├── credentials.go
│ └── types.go
└── zendure_decorators.go
├── package-lock.json
├── package.json
├── packaging
├── docker
│ └── bin
│ │ └── entrypoint.sh
├── fly.toml
├── gokrazy
│ └── config.tmpl.json
├── init
│ └── evcc.service
├── patch
│ └── asn1.diff
└── scripts
│ ├── postinstall.sh
│ ├── postremove.sh
│ ├── preinstall.sh
│ └── preremove.sh
├── playwright.config.ts
├── plugin
├── auth
│ ├── config.go
│ ├── oauth.go
│ ├── oauth_test.go
│ └── viessmann.go
├── calc.go
├── charger.go
├── combined.go
├── config.go
├── config_test.go
├── const.go
├── const_test.go
├── convert.go
├── error.go
├── getter.go
├── go.go
├── golang
│ ├── registry.go
│ └── stdlib
│ │ ├── fmt.go
│ │ ├── generate.go
│ │ ├── math.go
│ │ ├── strings.go
│ │ └── time.go
├── helper.go
├── http.go
├── http_auth.go
├── http_limit.go
├── http_test.go
├── ignore.go
├── javascript.go
├── javascript
│ └── registry.go
├── map.go
├── meter.go
├── method.go
├── method_enumer.go
├── modbus.go
├── mqtt.go
├── mqtt
│ ├── client.go
│ └── registry.go
├── mqtt_handler.go
├── mqtt_timeout.go
├── pipeline
│ ├── pipeline.go
│ └── pipeline_test.go
├── prometheus.go
├── random.go
├── script.go
├── sequence.go
├── sleep.go
├── sma.go
├── sma
│ ├── device.go
│ └── discover.go
├── socket.go
├── socket_test.go
├── sunspec.go
├── sunspec_cache.go
├── switch.go
├── timeseries.go
├── transformation.go
├── valid.go
└── watchdog.go
├── prettier.config.js
├── push
├── config.go
├── hub.go
├── ntfy.go
├── push.go
├── pushover.go
├── shoutrrr.go
└── telegram.go
├── schema.json
├── server
├── assets
│ ├── assets.go
│ └── assets_live.go
├── db
│ ├── cache
│ │ └── cache.go
│ ├── db.go
│ ├── log.go
│ └── settings
│ │ ├── api.go
│ │ ├── mock.go
│ │ ├── setting.go
│ │ └── settings_test.go
├── eebus
│ ├── certificate.go
│ ├── connector.go
│ ├── eebus.go
│ ├── eebus_test.go
│ ├── helper.go
│ └── types.go
├── helper.go
├── http.go
├── http_auth.go
├── http_config_device_handler.go
├── http_config_helper.go
├── http_config_helper_test.go
├── http_config_loadpoint_handler.go
├── http_config_metadata_handler.go
├── http_config_site_handler.go
├── http_config_site_other_handler.go
├── http_config_yaml_handler.go
├── http_global_settings_handler.go
├── http_loadpoint_handler.go
├── http_session_handler.go
├── http_site_handler.go
├── http_vehicle_handler.go
├── influxdb.go
├── influxdb_test.go
├── log.go
├── mcp
│ ├── mcp.go
│ ├── openapi.json
│ ├── openapi.md
│ ├── prompt.tpl
│ └── tools.go
├── modbus
│ ├── handler.go
│ ├── log.go
│ ├── proxy.go
│ ├── proxy_test.go
│ ├── readonlymode.go
│ └── readonlymode_enumer.go
├── mqtt.go
├── mqtt_setter.go
├── mqtt_test.go
├── openapi.go
├── openapi.yaml
├── openapi_test.go
├── product.go
├── providerauth
│ ├── handler.go
│ ├── providerauth.go
│ └── state.go
├── socket.go
├── socket_helper.go
├── socket_test.go
├── uds.go
├── uds_windows.go
└── updater
│ ├── github.go
│ ├── gokrazy.go
│ ├── run.go
│ ├── run_gokrazy.go
│ └── watch.go
├── tariff
├── amber.go
├── amber
│ └── types.go
├── awattar.go
├── awattar
│ └── api.go
├── combined.go
├── combined_test.go
├── config.go
├── corrently
│ ├── tokensource.go
│ └── types.go
├── edf-tempo.go
├── electricitymaps.go
├── elering.go
├── elering
│ └── types.go
├── embed.go
├── entsoe.go
├── entsoe
│ ├── api.go
│ ├── areas.go
│ └── static.go
├── fixed.go
├── fixed
│ ├── day.go
│ ├── day_enumer.go
│ ├── day_test.go
│ ├── month.go
│ ├── month_enumer.go
│ ├── timerange.go
│ ├── timerange_test.go
│ ├── zone.go
│ └── zone_test.go
├── fixed_test.go
├── groupe-e.go
├── gruenstromindex.go
├── helper.go
├── helper_test.go
├── ngeso.go
├── ngeso
│ └── api.go
├── octopus.go
├── octopus
│ ├── graphql
│ │ ├── api.go
│ │ ├── api_test.go
│ │ ├── errors.go
│ │ └── types.go
│ └── rest
│ │ └── api.go
├── octopus_test.go
├── ostrom.go
├── ostrom
│ └── api.go
├── proxy.go
├── proxy_average.go
├── proxy_average_test.go
├── proxy_cache.go
├── proxy_cache_error.go
├── proxy_cache_helper.go
├── pun.go
├── slots.go
├── slots_test.go
├── smartenergy.go
├── smartenergy
│ └── types.go
├── solcast.go
├── solcast
│ └── types.go
├── stekker.go
├── tariff.go
├── tariffs.go
├── template.go
├── template_test.go
├── tibber.go
├── types.go
├── types_test.go
└── wrapper.go
├── templates
├── README.md
├── definition
│ ├── charger
│ │ ├── abb.yaml
│ │ ├── abl-em4.yaml
│ │ ├── abl.yaml
│ │ ├── ac-elwa-2.yaml
│ │ ├── ac-elwa-e.yaml
│ │ ├── ac-thor.yaml
│ │ ├── alfen.yaml
│ │ ├── alphatec.yaml
│ │ ├── alpitronic.yaml
│ │ ├── amperfied-solar.yaml
│ │ ├── amperfied.yaml
│ │ ├── bender-cc.yaml
│ │ ├── bender-icc.yaml
│ │ ├── cfos.yaml
│ │ ├── compleo-duo.yaml
│ │ ├── compleo-solo.yaml
│ │ ├── dadapower.yaml
│ │ ├── daheimladen-pro.yaml
│ │ ├── daheimladen.yaml
│ │ ├── daikin-homehub-air2air.yaml
│ │ ├── daikin-homehub.yaml
│ │ ├── delta.yaml
│ │ ├── demo-charger.yaml
│ │ ├── demo-heatpump.yaml
│ │ ├── easee.yaml
│ │ ├── eebus.yaml
│ │ ├── elli-2.yaml
│ │ ├── elli-charger-connect.yaml
│ │ ├── elli-charger-pro.yaml
│ │ ├── em2go-duo.yaml
│ │ ├── em2go-home.yaml
│ │ ├── em2go.yaml
│ │ ├── emsesp.yaml
│ │ ├── eprowallbox.yaml
│ │ ├── etrel-duo.yaml
│ │ ├── etrel.yaml
│ │ ├── evbox-livo.yaml
│ │ ├── evecube.yaml
│ │ ├── evse-din.yaml
│ │ ├── evsewifi.yaml
│ │ ├── fritzdect.yaml
│ │ ├── fronius-wattpilot.yaml
│ │ ├── ghost.yaml
│ │ ├── go-e-v3.yaml
│ │ ├── go-e.yaml
│ │ ├── hardybarth-ecb1.yaml
│ │ ├── hardybarth-salia.yaml
│ │ ├── heidelberg.yaml
│ │ ├── hesotec.yaml
│ │ ├── homeassistant-switch.yaml
│ │ ├── homematic.yaml
│ │ ├── homewizard.yaml
│ │ ├── icharge-cion.yaml
│ │ ├── idm.yaml
│ │ ├── innogy-ebox.yaml
│ │ ├── kathrein.yaml
│ │ ├── keba-modbus-p40.yaml
│ │ ├── keba-modbus.yaml
│ │ ├── keba-udp.yaml
│ │ ├── kermi.yaml
│ │ ├── kse.yaml
│ │ ├── lambda-zewotherm.yaml
│ │ ├── lg-therma.yaml
│ │ ├── luxtronik.yaml
│ │ ├── mennekes-compact.yaml
│ │ ├── mennekes-hcc3.yaml
│ │ ├── mystrom.yaml
│ │ ├── neoom-n-plus.yaml
│ │ ├── neoom-n.yaml
│ │ ├── nrggen2.yaml
│ │ ├── nrgkick-bluetooth.yaml
│ │ ├── nrgkick-connect.yaml
│ │ ├── obo.yaml
│ │ ├── ochsner-bwwp.yaml
│ │ ├── ocpp-abb-tac.yaml
│ │ ├── ocpp-abl.yaml
│ │ ├── ocpp-alfen.yaml
│ │ ├── ocpp-autel.yaml
│ │ ├── ocpp-autoaid.yaml
│ │ ├── ocpp-beny.yaml
│ │ ├── ocpp-chargeamps.yaml
│ │ ├── ocpp-elecq.yaml
│ │ ├── ocpp-enercab.yaml
│ │ ├── ocpp-enplus.yaml
│ │ ├── ocpp-entratek.yaml
│ │ ├── ocpp-esolutions.yaml
│ │ ├── ocpp-evbox-elvi.yaml
│ │ ├── ocpp-goe.yaml
│ │ ├── ocpp-homecharge.yaml
│ │ ├── ocpp-huawei.yaml
│ │ ├── ocpp-mennekes-acu.yaml
│ │ ├── ocpp-orbis.yaml
│ │ ├── ocpp-sungrow.yaml
│ │ ├── ocpp-wallbox-fw5.yaml
│ │ ├── ocpp-wallbox.yaml
│ │ ├── ocpp-zaptec.yaml
│ │ ├── ocpp.yaml
│ │ ├── openevse.yaml
│ │ ├── openwb-2.0.yaml
│ │ ├── openwb-pro.yaml
│ │ ├── openwb.yaml
│ │ ├── pantabox.yaml
│ │ ├── pcelectric-garo.yaml
│ │ ├── peblar.yaml
│ │ ├── phoenix-charx.yaml
│ │ ├── phoenix-em-eth.yaml
│ │ ├── phoenix-ev-eth.yaml
│ │ ├── phoenix-ev-ser.yaml
│ │ ├── plugchoice.yaml
│ │ ├── porsche-pmcc.yaml
│ │ ├── porsche-pmcp.yaml
│ │ ├── porsche-wallbox.yaml
│ │ ├── pracht-alpha.yaml
│ │ ├── pulsares.yaml
│ │ ├── pulsatrix.yaml
│ │ ├── scheider-evlink-v3.yaml
│ │ ├── semp-sma.yaml
│ │ ├── semp.yaml
│ │ ├── senec-plus.yaml
│ │ ├── senec-premium.yaml
│ │ ├── shelly.yaml
│ │ ├── sigenergy.yaml
│ │ ├── smaevcharger.yaml
│ │ ├── smartevse.yaml
│ │ ├── smartwb.yaml
│ │ ├── solax-g2.yaml
│ │ ├── solax.yaml
│ │ ├── stiebel-lwa.yaml
│ │ ├── stiebel-wpm.yaml
│ │ ├── sungrow.yaml
│ │ ├── tapo.yaml
│ │ ├── tasmota.yaml
│ │ ├── tessie.yaml
│ │ ├── tinkerforge-warp.yaml
│ │ ├── tinkerforge-warp3-smart.yaml
│ │ ├── tinkerforge-warp3.yaml
│ │ ├── tplink.yaml
│ │ ├── twc3.yaml
│ │ ├── v2c.yaml
│ │ ├── vaillant.yaml
│ │ ├── vehicle-api.yaml
│ │ ├── versicharge.yaml
│ │ ├── vestel.yaml
│ │ ├── victron-evcs.yaml
│ │ ├── victron.yaml
│ │ ├── viessmann.yaml
│ │ ├── volttime.yaml
│ │ ├── wallbe-meter.yaml
│ │ ├── wallbe-pre2019-meter.yaml
│ │ ├── wallbe-pre2019.yaml
│ │ ├── wallbe.yaml
│ │ ├── webasto-next.yaml
│ │ ├── weidmüller.yaml
│ │ ├── weishaupt-wpm.yaml
│ │ └── zaptec.yaml
│ ├── common-schema.json
│ ├── defaults-schema.json
│ ├── devices-schema.json
│ ├── embed.go
│ ├── meter
│ │ ├── abb-ab.yaml
│ │ ├── ac-elwa-2.yaml
│ │ ├── ac-elwa-e.yaml
│ │ ├── acrel-adw300.yaml
│ │ ├── alpha-ess-smile.yaml
│ │ ├── apsystems-ez1.yaml
│ │ ├── batterx.yaml
│ │ ├── be-mpm3pm.yaml
│ │ ├── bgetech-ds100.yaml
│ │ ├── bgetech-ws100.yaml
│ │ ├── bosch-bpt.yaml
│ │ ├── cfos.yaml
│ │ ├── cg-em24.yaml
│ │ ├── cg-em24_e1.yaml
│ │ ├── cg-emt1xx.yaml
│ │ ├── cg-emt3xx.yaml
│ │ ├── cozify.yaml
│ │ ├── demo-battery.yaml
│ │ ├── demo-meter.yaml
│ │ ├── deye-hybrid-3p.yaml
│ │ ├── deye-hybrid-hp3.yaml
│ │ ├── deye-mi.yaml
│ │ ├── deye-storage.yaml
│ │ ├── deye-string.yaml
│ │ ├── discovergy.yaml
│ │ ├── dsmr.yaml
│ │ ├── dsmrlogger-aandewiel.yaml
│ │ ├── dzg.yaml
│ │ ├── e3dc-modbus.yaml
│ │ ├── e3dc-rscp.yaml
│ │ ├── eastron-sdm120.yaml
│ │ ├── eastron-sdm220_230.yaml
│ │ ├── eastron-sdm72.yaml
│ │ ├── eastron-sdm72v2_630.yaml
│ │ ├── eebus-mcp.yaml
│ │ ├── eebus-mgcp.yaml
│ │ ├── enphase.yaml
│ │ ├── esphome-dlms-austria.yaml
│ │ ├── fox-ess-avocado.yaml
│ │ ├── fox-ess-h1.yaml
│ │ ├── fox-ess-h3-smart.yaml
│ │ ├── fox-ess-h3.yaml
│ │ ├── fritzdect.yaml
│ │ ├── fritzgrid.yaml
│ │ ├── fronius-gen24.yaml
│ │ ├── fronius-ohmpilot.yaml
│ │ ├── fronius-solarapi-v1.yaml
│ │ ├── fronius-vertoplus.yaml
│ │ ├── go-e-controller.yaml
│ │ ├── goodwe-dt.yaml
│ │ ├── goodwe-hybrid.yaml
│ │ ├── goodwe-wifi.yaml
│ │ ├── growatt-hybrid-tlxh.yaml
│ │ ├── growatt-hybrid.yaml
│ │ ├── hager-flow-modbus.yaml
│ │ ├── homeassistant.yaml
│ │ ├── homematic.yaml
│ │ ├── homewizard-kwh.yaml
│ │ ├── homewizard-p1.yaml
│ │ ├── hoymiles-ahoydtu.yaml
│ │ ├── hoymiles-dtugateway.yaml
│ │ ├── hoymiles-opendtu.yaml
│ │ ├── huawei-emma.yaml
│ │ ├── huawei-smartlogger.yaml
│ │ ├── huawei-sun2000-dongle.yaml
│ │ ├── huawei-sun2000.yaml
│ │ ├── iammeter.yaml
│ │ ├── inepro.yaml
│ │ ├── iometer.yaml
│ │ ├── janitza.yaml
│ │ ├── keba-kecontact.yaml
│ │ ├── kostal-ksem-inverter.yaml
│ │ ├── kostal-ksem.yaml
│ │ ├── kostal-piko-hybrid.yaml
│ │ ├── kostal-piko-legacy.yaml
│ │ ├── kostal-piko-mp-plus.yaml
│ │ ├── kostal-piko-pv.yaml
│ │ ├── kostal-plenticore-gen2.yaml
│ │ ├── kostal-plenticore.yaml
│ │ ├── lg-ess-home-15.yaml
│ │ ├── lg-ess-home-8-10.yaml
│ │ ├── loxone.yaml
│ │ ├── marstek-jupiterc-plus.yaml
│ │ ├── marstek-venus.yaml
│ │ ├── mtec-eb-gen2.yaml
│ │ ├── mtec-eb-gen3.yaml
│ │ ├── mypv-wifi-meter.yaml
│ │ ├── mystrom.yaml
│ │ ├── openems.yaml
│ │ ├── orno.yaml
│ │ ├── p1monitor.yaml
│ │ ├── plexlog.yaml
│ │ ├── powerdog.yaml
│ │ ├── powerfox-poweropti.yaml
│ │ ├── qcells-hybrid-cloud.yaml
│ │ ├── rct-power.yaml
│ │ ├── saj-h1.yaml
│ │ ├── saj-h2.yaml
│ │ ├── saj-r5.yaml
│ │ ├── sax.yaml
│ │ ├── sbc-axx3.yaml
│ │ ├── schneider-iem3000.yaml
│ │ ├── senec-home.yaml
│ │ ├── senergy.yaml
│ │ ├── shelly-1pm.yaml
│ │ ├── shelly-3em.yaml
│ │ ├── shelly-pro-3em.yaml
│ │ ├── siemens-7kt1665.yaml
│ │ ├── siemens-junelight.yaml
│ │ ├── siemens-pac2200.yaml
│ │ ├── sigenergy.yaml
│ │ ├── slimmelezer-luxembourg.yaml
│ │ ├── slimmelezer-v2.yaml
│ │ ├── slimmelezer.yaml
│ │ ├── sma-datamanager.yaml
│ │ ├── sma-energymeter.yaml
│ │ ├── sma-homemanager.yaml
│ │ ├── sma-hybrid.yaml
│ │ ├── sma-inverter-modbus.yaml
│ │ ├── sma-inverter-speedwire.yaml
│ │ ├── sma-sbs-15-25-modbus.yaml
│ │ ├── sma-sbs-modbus.yaml
│ │ ├── sma-si-modbus.yaml
│ │ ├── sma-webbox.yaml
│ │ ├── smartfox-em2.yaml
│ │ ├── smartfox.yaml
│ │ ├── sofarsolar-g3.yaml
│ │ ├── sofarsolar.yaml
│ │ ├── solaranzeige-mqtt.yaml
│ │ ├── solaredge-hybrid.yaml
│ │ ├── solaredge-inverter.yaml
│ │ ├── solarlog.yaml
│ │ ├── solarman.yaml
│ │ ├── solarmax-inverter-smt.yaml
│ │ ├── solarmax-maxstorage.yaml
│ │ ├── solarwatt-flex.yaml
│ │ ├── solarwatt-myreserve-matrix.yaml
│ │ ├── solarwatt.yaml
│ │ ├── solax-hybrid-cloud.yaml
│ │ ├── solax-inverter-cloud.yaml
│ │ ├── solax.yaml
│ │ ├── solis-hybrid-s.yaml
│ │ ├── solis-hybrid.yaml
│ │ ├── solis.yaml
│ │ ├── sonnenbatterie.yaml
│ │ ├── sonnenbatterie_eco56.yaml
│ │ ├── storaxe.yaml
│ │ ├── sungrow-hybrid.yaml
│ │ ├── sungrow-ihm.yaml
│ │ ├── sungrow-inverter.yaml
│ │ ├── sunspec-battery-control.yaml
│ │ ├── sunspec-hybrid.yaml
│ │ ├── sunspec-inverter-control.yaml
│ │ ├── sunspec-inverter.yaml
│ │ ├── sunspec-meter.yaml
│ │ ├── tapo.yaml
│ │ ├── tasmota-3p.yaml
│ │ ├── tasmota-sml.yaml
│ │ ├── tasmota.yaml
│ │ ├── tesla-powerwall.yaml
│ │ ├── thor.yaml
│ │ ├── tibber-pulse.yaml
│ │ ├── tplink.yaml
│ │ ├── tq-em.yaml
│ │ ├── tq-em420.yaml
│ │ ├── varta.yaml
│ │ ├── victron-energy.yaml
│ │ ├── volkszaehler-http.yaml
│ │ ├── volkszaehler-importexport.yaml
│ │ ├── volkszaehler-ws.yaml
│ │ ├── vzlogger.yaml
│ │ ├── wago-879-30xx.yaml
│ │ ├── wattsonic-gen3.yaml
│ │ ├── wattsonic.yaml
│ │ ├── youless.yaml
│ │ └── zendure.yaml
│ ├── tariff
│ │ ├── allinpower.yaml
│ │ ├── amber.yaml
│ │ ├── api-akkudoktor-de.yaml
│ │ ├── awattar.yaml
│ │ ├── demo-co2-forecast.yaml
│ │ ├── demo-dynamic-grid.yaml
│ │ ├── demo-solar-forecast.yaml
│ │ ├── electricitymaps-free.yaml
│ │ ├── electricitymaps.yaml
│ │ ├── elering.yaml
│ │ ├── energinet-co2.yaml
│ │ ├── energinet-price.yaml
│ │ ├── energinet.yaml
│ │ ├── energy-charts-api.yaml
│ │ ├── energyforecast.yaml
│ │ ├── enever.yaml
│ │ ├── entsoe.yaml
│ │ ├── ews.yaml
│ │ ├── forecast-solar.yaml
│ │ ├── green-grid-compass.yaml
│ │ ├── groupe-e.yaml
│ │ ├── gruenstromindex.yaml
│ │ ├── ned.yaml
│ │ ├── ngeso.yaml
│ │ ├── nordpool.yaml
│ │ ├── octopus-api.yaml
│ │ ├── octopus-productcode.yaml
│ │ ├── open-meteo.yaml
│ │ ├── ostrom.yaml
│ │ ├── pun.yaml
│ │ ├── smartenergy.yaml
│ │ ├── solarprognose.yaml
│ │ ├── solcast.yaml
│ │ ├── spottyenergy.yaml
│ │ ├── stekker.yaml
│ │ ├── tibber.yaml
│ │ └── victron.yaml
│ └── vehicle
│ │ ├── aiways.yaml
│ │ ├── audi.yaml
│ │ ├── bmw.yaml
│ │ ├── cardata.yaml
│ │ ├── carwings.yaml
│ │ ├── citroen.yaml
│ │ ├── dacia.yaml
│ │ ├── ds.yaml
│ │ ├── evnotify.yaml
│ │ ├── fiat.yaml
│ │ ├── flobz.yaml
│ │ ├── ford-connect.yaml
│ │ ├── ford.yaml
│ │ ├── homeassistant.yaml
│ │ ├── hyundai.yaml
│ │ ├── ioBroker.bmw.yaml
│ │ ├── iso15118.yaml
│ │ ├── jaguar-landrover.yaml
│ │ ├── kia.yaml
│ │ ├── mazda2mqtt.yaml
│ │ ├── mercedes.yaml
│ │ ├── mg.yaml
│ │ ├── mg2mqtt.yaml
│ │ ├── mini.yaml
│ │ ├── mz2mqtt.yaml
│ │ ├── nissan-ariya.yaml
│ │ ├── nissan.yaml
│ │ ├── niu-e-scooter.yaml
│ │ ├── offline.yaml
│ │ ├── opel.yaml
│ │ ├── ovms.yaml
│ │ ├── peugeot.yaml
│ │ ├── polestar.yaml
│ │ ├── porsche.yaml
│ │ ├── renault.yaml
│ │ ├── seat-cupra.yaml
│ │ ├── seat.yaml
│ │ ├── skoda.yaml
│ │ ├── smart-hello.yaml
│ │ ├── smart.yaml
│ │ ├── tesla-ble.yaml
│ │ ├── tesla.yaml
│ │ ├── teslafi.yaml
│ │ ├── teslalogger.yaml
│ │ ├── teslamate.yaml
│ │ ├── tessie.yaml
│ │ ├── toyota.yaml
│ │ ├── tronity.yaml
│ │ ├── volvo-connected.yaml
│ │ ├── volvo2mqtt.yaml
│ │ ├── vw.yaml
│ │ └── zero.yaml
└── evcc.io
│ └── .gitignore
├── tests
├── auth.spec.ts
├── backup-restore.spec.ts
├── basics.evcc.yaml
├── basics.spec.ts
├── battery-settings-co2.evcc.yaml
├── battery-settings-co2.spec.ts
├── battery-settings.evcc.yaml
├── battery-settings.spec.ts
├── boot.spec.ts
├── config-aux.spec.ts
├── config-battery.spec.ts
├── config-circuit.evcc.yaml
├── config-circuit.spec.ts
├── config-empty.evcc.yaml
├── config-ext-meter.spec.ts
├── config-fatals.spec.ts
├── config-grid-only.evcc.yaml
├── config-grid.spec.ts
├── config-invalid-template.spec.ts
├── config-invalid-template.sql
├── config-loadpoint.spec.ts
├── config-messaging.spec.ts
├── config-modbus-fields.spec.ts
├── config-modbus-fields.sql
├── config-mqtt.spec.ts
├── config-onboarding.spec.ts
├── config-one-lp.evcc.yaml
├── config-pv.spec.ts
├── config-shm.spec.ts
├── config-tariffs.spec.ts
├── config-vehicles.spec.ts
├── config-with-tariffs.evcc.yaml
├── config-with-vehicle.evcc.yaml
├── config.spec.ts
├── currents.spec.ts
├── custom-css.css
├── custom-css.spec.ts
├── demo.spec.ts
├── evcc.ts
├── fast.evcc.yaml
├── fatal-db.evcc.yaml
├── fatal-syntax.evcc.yaml
├── fatal.spec.ts
├── heating.evcc.yaml
├── heating.spec.ts
├── hems.spec.ts
├── issue.evcc.yaml
├── issue.spec.ts
├── limits.spec.ts
├── loadpoint-sort.evcc.yaml
├── loadpoint-sort.spec.ts
├── logs.spec.ts
├── modals.spec.ts
├── mqtt.ts
├── password.sql
├── plan-fixed-tariff.evcc.yaml
├── plan.evcc.yaml
├── plan.spec.ts
├── sessions.evcc.yaml
├── sessions.spec.ts
├── sessions.sql
├── simulator.evcc.yaml
├── simulator.ts
├── simulator
│ ├── api.ts
│ ├── index.html
│ ├── src
│ │ ├── Simulator.vue
│ │ └── main.ts
│ └── vite.config.ts
├── smart-cost-only.evcc.yaml
├── smart-cost-only.spec.ts
├── smart-cost.spec.ts
├── smart-feedin.evcc.yaml
├── smart-feedin.spec.ts
├── sponsor.evcc.yaml
├── sponsor.spec.ts
├── sponsor.sql
├── statistics.evcc.yaml
├── statistics.spec.ts
├── statistics.sql
├── utils.ts
├── vehicle-error.evcc.yaml
├── vehicle-error.spec.ts
├── vehicle-settings.spec.ts
└── ws.spec.ts
├── tsconfig.json
├── util
├── auth
│ ├── auth.go
│ └── auth_test.go
├── cache.go
├── cache_test.go
├── cloud
│ ├── api.go
│ └── client.go
├── config
│ ├── config.go
│ ├── device.go
│ ├── handler.go
│ ├── instance.go
│ └── types.go
├── config_redactor.go
├── decoder.go
├── decoder_test.go
├── duration.go
├── encode
│ ├── encode.go
│ └── encode_test.go
├── env.go
├── format.go
├── format_functions.go
├── format_test.go
├── homeassistant
│ └── connection.go
├── jq
│ └── jq.go
├── locale
│ ├── internal
│ │ └── types.go
│ ├── locale.go
│ └── locale_test.go
├── log.go
├── log_context.go
├── log_redactor.go
├── log_test.go
├── logstash
│ ├── element.go
│ ├── levels.go
│ ├── log.go
│ └── log_test.go
├── machine
│ ├── machine.go
│ └── machine_test.go
├── metering.go
├── modbus
│ ├── connection.go
│ ├── functions.go
│ ├── log.go
│ ├── modbus.go
│ ├── modbus_test.go
│ ├── mutex.go
│ ├── register.go
│ ├── register_test.go
│ └── sunspec.go
├── monitor.go
├── monitor_test.go
├── net.go
├── net_test.go
├── oauth
│ ├── helper.go
│ ├── tokensource.go
│ └── tokensource_test.go
├── param.go
├── param_shard.go
├── param_test.go
├── pipe
│ ├── limiter.go
│ └── limiter_test.go
├── queue.go
├── registry
│ └── registry.go
├── request
│ ├── functions.go
│ ├── helper.go
│ ├── json.go
│ ├── redirect.go
│ ├── roundtrip.go
│ └── xml.go
├── shortrfc3339
│ ├── shortrfc3339.go
│ └── shortrfc3339_test.go
├── sponsor
│ ├── auth.go
│ ├── pulsares.go
│ └── victron.go
├── tee.go
├── telemetry
│ ├── charge.go
│ └── types.go
├── templates
│ ├── class.go
│ ├── class_enumer.go
│ ├── defaults.go
│ ├── defaults.yaml
│ ├── documentation.go
│ ├── documentation.tpl
│ ├── documentation_modbus.tpl
│ ├── generate
│ │ └── main.go
│ ├── includes
│ │ ├── eebus.tpl
│ │ ├── mqtt.tpl
│ │ ├── ocpp.tpl
│ │ ├── switchsocket.tpl
│ │ ├── tariff-base.tpl
│ │ ├── tariff-features.tpl
│ │ ├── vehicle-base.tpl
│ │ ├── vehicle-common.tpl
│ │ ├── vehicle-features.tpl
│ │ └── vehicle-language.tpl
│ ├── init.go
│ ├── merge.go
│ ├── merge_test.go
│ ├── modbus.tpl
│ ├── paramtype.go
│ ├── paramtype_enumer.go
│ ├── proxy.tpl
│ ├── render_instance.go
│ ├── render_testing.go
│ ├── template.go
│ ├── template_modbus.go
│ ├── template_test.go
│ ├── types.go
│ ├── usage.go
│ ├── usage_enumer.go
│ ├── utils.go
│ └── utils_test.go
├── test
│ ├── ci.go
│ └── errors.go
├── time.go
├── token.go
├── transport
│ ├── basicauth.go
│ ├── bearer.go
│ ├── decorator.go
│ ├── decorators.go
│ └── default.go
├── urlvalues
│ └── url.go
└── version.go
├── vehicle
├── aiways.go
├── aiways
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── audi.go
├── audi
│ ├── etron
│ │ ├── api.go
│ │ ├── params.go
│ │ └── types.go
│ ├── params.go
│ └── samples
│ │ └── status.json
├── bluelink.go
├── bluelink
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── bmw
│ ├── cardata
│ │ ├── api.go
│ │ ├── mqtt.go
│ │ ├── provider.go
│ │ ├── provider_test.go
│ │ ├── token.go
│ │ └── types.go
│ └── connected
│ │ ├── api.go
│ │ ├── identity.go
│ │ ├── param.go
│ │ ├── provider.go
│ │ └── types.go
├── bmw_deprecated.go
├── cardata.go
├── carwings.go
├── cloud.go
├── config.go
├── embed.go
├── fiat.go
├── fiat
│ ├── api.go
│ ├── controller.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── ford-connect.go
├── ford.go
├── ford
│ ├── api.go
│ ├── autonomic
│ │ ├── api.go
│ │ ├── identity.go
│ │ └── types.go
│ ├── connect
│ │ ├── api.go
│ │ ├── identity.go
│ │ ├── provider.go
│ │ └── types.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── helper.go
├── homeassistant.go
├── jlr.go
├── jlr
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── mb
│ └── identity.go
├── mercedes.go
├── mercedes
│ ├── api.go
│ ├── helper.go
│ ├── identity.go
│ ├── pb
│ │ ├── acp.pb.go
│ │ ├── client.pb.go
│ │ ├── cluster.pb.go
│ │ ├── eventpush.pb.go
│ │ ├── protos
│ │ │ └── protos.pb.go
│ │ ├── service-activation.pb.go
│ │ ├── user-events.pb.go
│ │ ├── vehicle-commands.pb.go
│ │ ├── vehicle-events.pb.go
│ │ ├── vehicleapi.pb.go
│ │ └── vin-events.pb.go
│ ├── provider.go
│ └── types.go
├── mg.go
├── nissan.go
├── nissan
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── niu.go
├── niu
│ ├── types.go
│ └── types_test.go
├── ovms.go
├── ovms
│ └── types.go
├── polestar.go
├── polestar
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ ├── query.gql
│ └── types.go
├── porsche.go
├── porsche
│ ├── api.go
│ ├── api_emobility.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── psa.go
├── psa
│ ├── api.go
│ ├── duration.go
│ ├── helper.go
│ ├── identity.go
│ ├── oauth2.go
│ ├── provider.go
│ └── types.go
├── renault.go
├── renault
│ ├── gigya
│ │ └── identity.go
│ ├── kamereon
│ │ ├── api.go
│ │ └── types.go
│ ├── keys
│ │ └── keys.go
│ └── provider.go
├── saic
│ ├── api.go
│ ├── identity.go
│ ├── provider.go
│ └── requests
│ │ ├── api_config.go
│ │ ├── basic_encryption.go
│ │ ├── encryption.go
│ │ ├── hashUtils.go
│ │ ├── macUtils.go
│ │ ├── sendRequest.go
│ │ └── types.go
├── seat-cupra.go
├── seat.go
├── seat
│ ├── api.go
│ ├── cupra
│ │ ├── api.go
│ │ ├── params.go
│ │ ├── provider.go
│ │ └── types.go
│ └── params.go
├── skoda.go
├── skoda
│ ├── api.go
│ ├── params.go
│ ├── provider.go
│ ├── service
│ │ └── tokenrefreshservice.go
│ ├── tokenrefreshservice
│ │ └── endpoint.go
│ └── types.go
├── smart-hello.go
├── smart
│ ├── api.go
│ ├── hello
│ │ ├── api.go
│ │ ├── const.go
│ │ ├── helper.go
│ │ ├── identity.go
│ │ ├── provider.go
│ │ ├── types.go
│ │ └── types_test.go
│ ├── provider.go
│ └── types.go
├── template.go
├── template_test.go
├── tesla.go
├── tesla
│ ├── api_test.go
│ ├── controller.go
│ ├── helper.go
│ ├── helper_test.go
│ ├── identity.go
│ ├── provider.go
│ └── types.go
├── toyota.go
├── toyota
│ ├── api.go
│ ├── api_test.go
│ ├── identity.go
│ ├── identity_test.go
│ ├── provider.go
│ └── types.go
├── tronity.go
├── tronity
│ ├── auth.go
│ └── types.go
├── tronity_decorators.go
├── types.go
├── vag
│ ├── aazsproxy
│ │ └── endpoint.go
│ ├── cariad
│ │ └── const.go
│ ├── challenge.go
│ ├── idkproxy
│ │ └── endpoint.go
│ ├── loginapps
│ │ ├── endpoint.go
│ │ ├── token.go
│ │ └── token_test.go
│ ├── mbb
│ │ └── endpoint.go
│ ├── service
│ │ ├── azs.go
│ │ └── mbb.go
│ ├── token.go
│ ├── token_test.go
│ ├── tokensource.go
│ ├── tokensource_test.go
│ └── vwidentity
│ │ ├── endpoint.go
│ │ ├── forms.go
│ │ ├── forms_test.go
│ │ └── oauth2.go
├── vehicle.go
├── vehicle_decorators.go
├── volvo-connected.go
├── volvo
│ ├── connected
│ │ ├── api.go
│ │ ├── oauth2.go
│ │ ├── provider.go
│ │ └── types.go
│ └── types.go
├── vw.go
├── vw
│ ├── api.go
│ ├── id
│ │ ├── api.go
│ │ ├── params.go
│ │ ├── provider.go
│ │ └── types.go
│ ├── params.go
│ ├── provider.go
│ ├── samples
│ │ ├── rolesrights_egolf.json
│ │ └── rolesrights_q55.json
│ ├── types.go
│ ├── types_rolesrights.go
│ ├── types_status.go
│ └── types_test.go
├── wrapper.go
├── zero
│ ├── api.go
│ ├── provider.go
│ └── types.go
└── zeromotorcycles.go
├── vite.config.ts
└── vitest.config.ts
/.cursorrules:
--------------------------------------------------------------------------------
1 | AGENTS.md
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .storybook
3 | .vscode
4 | *.conf
5 | *.Dockerfile
6 | *.gz
7 | *.image
8 | *.json
9 | *.sh
10 | *.yaml
11 | Dockerfile
12 | evcc
13 | evcc.exe
14 | builddir
15 | node_modules
16 | release
17 | !entrypoint.sh
18 | !evcc.dist.yaml
19 | !package*.json
20 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; indicate this is the root of the project
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 |
7 | end_of_line = LF
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | indent_style = tab
12 | indent_size = 4
13 |
14 | [*.css]
15 | indent_size = 2
16 |
17 | [*.{js,html,yml,yaml,json,md,ts}]
18 | indent_style = space
19 | indent_size = 2
20 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: evcc-io
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Need help?
4 | url: https://github.com/evcc-io/evcc/discussions/categories/need-help
5 | about: GitHub community discussions is a good place to ask questions.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/copilot-instructions.md:
--------------------------------------------------------------------------------
1 | ../AGENTS.md
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "monthly"
7 | labels:
8 | - "infrastructure"
9 |
--------------------------------------------------------------------------------
/.github/issue_label_bot.yaml:
--------------------------------------------------------------------------------
1 | label-alias:
2 | bug: "bug"
3 | feature_request: "enhancement"
4 | question: "question"
5 |
--------------------------------------------------------------------------------
/.github/workflows/openapi-validate.yml:
--------------------------------------------------------------------------------
1 | name: openapi-validate
2 |
3 | permissions:
4 | contents: read
5 |
6 | on:
7 | workflow_dispatch:
8 | pull_request:
9 | paths:
10 | - "server/openapi.yaml"
11 | - ".github/workflows/openapi-validate.yml"
12 |
13 | jobs:
14 | validate-openapi:
15 | name: Validate OpenAPI spec
16 | runs-on: depot-ubuntu-24.04-arm
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v5
20 | - name: Setup Go
21 | uses: actions/setup-go@v6
22 | with:
23 | go-version: stable
24 | - name: Validate OpenAPI spec
25 | run: go run github.com/getkin/kin-openapi/cmd/validate@v0.133.0 -- server/openapi.yaml
26 |
--------------------------------------------------------------------------------
/.github/workflows/schema.yml:
--------------------------------------------------------------------------------
1 | name: Validate Schema
2 |
3 | on:
4 | push:
5 | paths:
6 | - "*.yaml"
7 | - "*.json"
8 | pull_request:
9 | paths:
10 | - "*.yaml"
11 | - "*.json"
12 |
13 | jobs:
14 | build:
15 | runs-on: depot-ubuntu-24.04-arm
16 |
17 | permissions:
18 | contents: read
19 |
20 | steps:
21 | - uses: actions/checkout@v5
22 | with:
23 | persist-credentials: false
24 | - uses: nwisbeta/validate-yaml-schema@v2.0.0
25 | with:
26 | yamlSchemasJson: |
27 | {
28 | "./schema.json": ["evcc.dist.yaml"]
29 | }
30 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yaml:
--------------------------------------------------------------------------------
1 | name: "Stale issue handler"
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "0 0 * * *"
7 | issue_comment:
8 | types: [created]
9 |
10 | jobs:
11 | stale:
12 | permissions:
13 | contents: read
14 | issues: write
15 | pull-requests: write
16 | runs-on: depot-ubuntu-24.04-arm
17 | steps:
18 | - uses: actions/stale@v10
19 | id: stale
20 | with:
21 | days-before-stale: 7
22 | days-before-close: 5
23 | exempt-issue-labels: "pinned,security,backlog,bug"
24 | exempt-pr-labels: "pinned,security,backlog,bug"
25 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | tests/custom-css.css
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import { StorybookConfig } from "@storybook/vue3-vite";
2 |
3 | export default {
4 | stories: ["../assets/js/**/*.stories.@(js|ts)"],
5 | addons: ["@chromatic-com/storybook"],
6 | framework: {
7 | name: "@storybook/vue3-vite",
8 | options: {},
9 | },
10 | } satisfies StorybookConfig;
11 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "Vue.volar",
4 | "vitest.explorer",
5 | "golang.go",
6 | "esbenp.prettier-vscode",
7 | "yoavbls.pretty-ts-errors",
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSES/exclusions.md:
--------------------------------------------------------------------------------
1 | # MIT License Exclusions
2 |
3 | Sponsor-required components are **NOT** covered by the MIT License.
4 |
5 | See file license header for details.
6 | If you want to use them in your own project, one evcc sponsorship token is required per evcc instance.
7 | Custom licensing agreements are available - please [contact us](mailto:info@evcc.io) to discuss your specific requirements.
8 |
--------------------------------------------------------------------------------
/LICENSES/fonts.md:
--------------------------------------------------------------------------------
1 | # Font Licenses
2 |
3 | ## Montserrat Font
4 |
5 | - **Source**: https://github.com/JulietaUla/Montserrat
6 | - **License**: SIL Open Font License 1.1
7 | - **Files**: /assets/font/Montserrat-\*.woff2
8 |
--------------------------------------------------------------------------------
/LICENSES/icons.md:
--------------------------------------------------------------------------------
1 | # Icon Licenses
2 |
3 | ## Material Symbols
4 |
5 | - **Source**: https://github.com/google/material-design-icons
6 | - **License**: Apache License 2.0
7 | - **Files**: /assets/js/components/MaterialIcon/\*.vue
8 | - **Notes**: Repackaged as Vue components, some modified/adapted versions
9 |
10 | ## H2D2 Shopicons
11 |
12 | - **Source**: https://github.com/H2D2-Design/h2d2-shopicons
13 | - **License**: Apache License 2.0
14 | - **Usage**: Regular dependency
15 |
--------------------------------------------------------------------------------
/api/actionconfig_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestActionConfigString(t *testing.T) {
10 | var a ActionConfig
11 | assert.NotPanics(t, func() {
12 | _ = a.String()
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/api/batterymode.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | // BatteryMode is the home battery operation mode. Valid values are normal, locked and charge
4 | type BatteryMode int
5 |
6 | //go:generate go tool enumer -type BatteryMode -trimprefix Battery -transform=lower
7 | const (
8 | BatteryUnknown BatteryMode = iota
9 | BatteryNormal
10 | BatteryHold
11 | BatteryCharge
12 | )
13 |
--------------------------------------------------------------------------------
/api/feature.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | type Feature int
4 |
5 | //go:generate go tool enumer -type Feature -text
6 | const (
7 | _ Feature = iota
8 | CoarseCurrent // charger
9 | IntegratedDevice // charger
10 | Heating // charger
11 | Average // tariff
12 | Cacheable // tariff
13 | Offline // vehicle
14 | Retryable // vehicle
15 | Streaming // vehicle
16 | WelcomeCharge // vehicle
17 | )
18 |
--------------------------------------------------------------------------------
/api/marshal.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | // BytesMarshaler marshals into bytes/string representation
4 | type BytesMarshaler interface {
5 | MarshalBytes() ([]byte, error)
6 | }
7 |
8 | // StructMarshaler marshals into a struct representation
9 | type StructMarshaler interface {
10 | MarshalStruct() (any, error)
11 | }
12 |
--------------------------------------------------------------------------------
/api/plans.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | type RepeatingPlan struct {
4 | Weekdays []int `json:"weekdays"` // 0-6 (Sunday-Saturday)
5 | Time string `json:"time"` // HH:MM
6 | Tz string `json:"tz"` // timezone in IANA format
7 | Soc int `json:"soc"` // target soc
8 | Precondition int64 `json:"precondition"` // precondition duration in seconds
9 | Active bool `json:"active"` // active flag
10 | }
11 |
--------------------------------------------------------------------------------
/api/plugin.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | // Custom meter/charger/vehicle type
4 | const Custom = "custom"
5 |
--------------------------------------------------------------------------------
/api/proto/auth.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | // protoc proto/auth.proto --go_out=. --go-grpc_out=.
4 |
5 | import "google/protobuf/timestamp.proto";
6 |
7 | option go_package = "proto/pb";
8 |
9 | service Auth {
10 | rpc IsAuthorized (AuthRequest) returns (AuthReply) {}
11 | }
12 |
13 | message AuthRequest {
14 | string token = 1;
15 | }
16 |
17 | message AuthReply {
18 | bool authorized = 1;
19 | string subject = 2;
20 | google.protobuf.Timestamp expires_at = 3;
21 | }
22 |
--------------------------------------------------------------------------------
/api/proto/vehicle.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | // protoc proto/vehicle.proto --go_out=. --go-grpc_out=.
4 |
5 | option go_package = "proto/pb";
6 |
7 | service Vehicle {
8 | rpc New (NewRequest) returns (NewReply) {}
9 | rpc SoC (SoCRequest) returns (SoCReply) {}
10 | }
11 |
12 | message NewRequest {
13 | string token = 1;
14 | string type = 2;
15 | map<string,string> config = 3;
16 | }
17 |
18 | message NewReply {
19 | int64 vehicle_id = 1;
20 | }
21 |
22 | message SoCRequest {
23 | string token = 1;
24 | int64 vehicle_id = 2;
25 | }
26 |
27 | message SoCReply {
28 | double soc = 1;
29 | }
30 |
--------------------------------------------------------------------------------
/api/proto/victron.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | // protoc proto/victron.proto --go_out=. --go-grpc_out=.
4 |
5 | option go_package = "proto/pb";
6 |
7 | service Victron {
8 | rpc IsValidDevice (VictronRequest) returns (VictronReply) {}
9 | }
10 |
11 | message VictronRequest {
12 | string productId = 1;
13 | string vrmId = 2;
14 | string serial = 3;
15 | string board = 4;
16 | }
17 |
18 | message VictronReply {
19 | bool authorized = 1;
20 | string subject = 2;
21 | }
22 |
--------------------------------------------------------------------------------
/api/reason.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | type Reason int
4 |
5 | //go:generate go tool enumer -type Reason -trimprefix Reason -transform=lower
6 | const (
7 | ReasonUnknown Reason = iota
8 | ReasonWaitingForAuthorization
9 | ReasonDisconnectRequired
10 | )
11 |
--------------------------------------------------------------------------------
/api/store/types.go:
--------------------------------------------------------------------------------
1 | package store
2 |
3 | // Provider creates a Persister for given string key
4 | type Provider func(string) Store
5 |
6 | // Store can load and store data
7 | type Store interface {
8 | Load(any) error
9 | Save(any) error
10 | }
11 |
--------------------------------------------------------------------------------
/api/tariff.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | //go:generate go tool enumer -type TariffType -trimprefix TariffType -transform=lower -text
4 | //go:generate go tool enumer -type TariffUsage -trimprefix TariffUsage -transform=lower
5 |
6 | type TariffType int
7 |
8 | const (
9 | _ TariffType = iota
10 | TariffTypePriceStatic
11 | TariffTypePriceDynamic
12 | TariffTypePriceForecast
13 | TariffTypeCo2
14 | TariffTypeSolar
15 | )
16 |
17 | type TariffUsage int
18 |
19 | const (
20 | _ TariffUsage = iota
21 | TariffUsageCo2
22 | TariffUsageFeedIn
23 | TariffUsageGrid
24 | TariffUsagePlanner
25 | TariffUsageSolar
26 | )
27 |
--------------------------------------------------------------------------------
/assets/font/Montserrat-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/font/Montserrat-Bold.woff2
--------------------------------------------------------------------------------
/assets/font/Montserrat-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/font/Montserrat-Medium.woff2
--------------------------------------------------------------------------------
/assets/github/evcc-gopher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/github/evcc-gopher.png
--------------------------------------------------------------------------------
/assets/github/screenshot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/github/screenshot.webp
--------------------------------------------------------------------------------
/assets/js/components/Config/EebusModal.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <YamlModal
3 | id="eebusModal"
4 | :title="$t('config.eebus.title')"
5 | :description="$t('config.eebus.description')"
6 | docs="/docs/reference/configuration/eebus"
7 | :defaultYaml="defaultYaml"
8 | removeKey="eebus"
9 | endpoint="/config/eebus"
10 | @changed="$emit('changed')"
11 | />
12 | </template>
13 |
14 | <script>
15 | import YamlModal from "./YamlModal.vue";
16 | import defaultYaml from "./defaultYaml/eebus.yaml?raw";
17 |
18 | export default {
19 | name: "EebusModal",
20 | components: { YamlModal },
21 | emits: ["changed"],
22 | data() {
23 | return { defaultYaml: defaultYaml.trim() };
24 | },
25 | };
26 | </script>
27 |
--------------------------------------------------------------------------------
/assets/js/components/Config/ExperimentalBanner.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <div class="alert alert-warning my-4 pb-0" role="alert" data-testid="experimental-banner">
3 | <p>
4 | <strong>Experimental! 🧪</strong>
5 | The UI configuration is still in development. Found an issue? Please
6 | <a href="https://github.com/evcc-io/evcc/issues" target="_blank">report it on GitHub</a
7 | >. Need to reset? Delete <code>evcc.db</code> or run <code>evcc migrate --reset</code>.
8 | Note: Disabling experimental features only hides the UI features. Your configuration
9 | stays untouched.
10 | </p>
11 | </div>
12 | </template>
13 |
14 | <script>
15 | export default {
16 | name: "ExperimentalBanner",
17 | };
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/Config/HemsModal.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <YamlModal
3 | id="hemsModal"
4 | :title="$t('config.hems.title')"
5 | :description="$t('config.hems.description')"
6 | docs="/docs/features/14a-enwg-steuve"
7 | :defaultYaml="defaultYaml"
8 | endpoint="/config/hems"
9 | removeKey="hems"
10 | data-testid="hems-modal"
11 | @changed="$emit('changed')"
12 | />
13 | </template>
14 |
15 | <script>
16 | import YamlModal from "./YamlModal.vue";
17 | import defaultYaml from "./defaultYaml/hems.yaml?raw";
18 |
19 | export default {
20 | name: "HemsModal",
21 | components: { YamlModal },
22 | emits: ["changed"],
23 | data() {
24 | return { defaultYaml: defaultYaml.trim() };
25 | },
26 | };
27 | </script>
28 |
--------------------------------------------------------------------------------
/assets/js/components/Config/Markdown.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <!-- eslint-disable-next-line vue/no-v-html -->
3 | <div class="root" v-html="compiledMarkdown"></div>
4 | </template>
5 |
6 | <script>
7 | import snarkdown from "snarkdown";
8 |
9 | export default {
10 | name: "MarkdownRenderer",
11 | props: {
12 | markdown: String,
13 | },
14 | computed: {
15 | compiledMarkdown() {
16 | return snarkdown(this.markdown);
17 | },
18 | },
19 | };
20 | </script>
21 | <style scoped>
22 | .root {
23 | max-width: 100%;
24 | }
25 | .root :deep(pre.code) {
26 | overflow-x: auto;
27 | margin: 1em 0;
28 | hyphens: none;
29 | }
30 | </style>
31 |
--------------------------------------------------------------------------------
/assets/js/components/Config/defaultYaml/eebus.yaml:
--------------------------------------------------------------------------------
1 | #shipid: EVCC-1234567890abcdef
2 | #interfaces:
3 | # - eth0
4 | #certificate:
5 | # public: |
6 | # -----BEGIN CERTIFICATE-----
7 | # 1234567890abcdef==
8 | # -----END CERTIFICATE-----
9 | # private: |
10 | # -----BEGIN EC PRIVATE KEY-----
11 | # 1234567890abcdef
12 | # -----END EC PRIVATE KEY-----
13 |
--------------------------------------------------------------------------------
/assets/js/components/Config/defaultYaml/heatpump.yaml:
--------------------------------------------------------------------------------
1 | ## required attributes [type: heatpump]
2 |
3 | setmaxpower: # update the maximum heating power
4 | source: js
5 | script: console.log(maxpower); #
6 |
7 | ## optional attributes (read-only)
8 |
9 | #getmaxpower: # heating power in W
10 | # source: const
11 | # value: 5000
12 | #power: # heating power in W
13 | # source: const
14 | # value: 2000
15 | #energy: # meter reading in kWh
16 | # source: const
17 | # value: 42.5
18 | #temp: # current temperature (°C)
19 | # source: const
20 | # value: 45.5
21 | #limittemp: # temperature limit (°C) configured in device
22 | # source: const
23 | # value: 60
24 |
--------------------------------------------------------------------------------
/assets/js/components/Config/defaultYaml/hems.yaml:
--------------------------------------------------------------------------------
1 | ## external control via binary in put
2 |
3 | #type: relay
4 | #maxPower: 4200 # limit loadpoints to 4.2 kW total
5 | #limit: # limit signal, plugin
6 | # source: mqtt
7 | # topic: hems/limit/status # 0/false = normal, 1/true = limit active
8 |
9 | ## external control via EEBus protocol
10 |
11 | #type: eebus # general EEBus setup (cert gen) required
12 | #ski: "1234-5678-90AB-CDEF" # SKI of control box (grid operator)
13 |
--------------------------------------------------------------------------------
/assets/js/components/Config/defaultYaml/modbusproxy.yaml:
--------------------------------------------------------------------------------
1 | #- port: 5021 # proxy port for incoming ModbusTCP requests
2 | # uri: 192.0.2.2:502 # device address for downstream requests
3 | # rtu: true # optional: use RTU for downstream requests to device
4 | #- port: 5022 # proxy port for incoming ModbusTCP requests
5 | # device: /dev/ttyUSB0 # local device for downstream requests
6 | # baudrate: 9600
7 | # comset: "8N1"
8 |
--------------------------------------------------------------------------------
/assets/js/components/Config/defaultYaml/tariffs.yaml:
--------------------------------------------------------------------------------
1 | #currency: EUR
2 |
3 | #grid: # price using energy from the grid
4 | # type: fixed
5 | # price: 0.294 # EUR/kWh
6 |
7 | #feedin: # price for feeding solar energy to the grid
8 | # type: fixed
9 | # price: 0.08 # EUR/kWh
10 |
11 | #co2: # carbon intensity forecast
12 | # type: template
13 | # template: grünstromindex
14 | # zip: <zip>
15 |
16 | #solar: # list of pv generation forecast (additive)
17 | #- type: template
18 | # template: solcast
19 | # site: <site>
20 | # token: <token>
21 |
--------------------------------------------------------------------------------
/assets/js/components/Footer/Footer.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <footer class="footer" data-testid="footer">
3 | <div class="container py-2">
4 | <div class="d-flex justify-content-between">
5 | <Version v-bind="version" />
6 | <Savings v-bind="savings" />
7 | </div>
8 | </div>
9 | </footer>
10 | </template>
11 |
12 | <script lang="ts">
13 | import "@h2d2/shopicons/es/filled/testtube";
14 |
15 | import Version from "./Version.vue";
16 | import Savings from "../Savings/Savings.vue";
17 | import { defineComponent } from "vue";
18 |
19 | export default defineComponent({
20 | name: "Footer",
21 | components: { Version, Savings },
22 | props: {
23 | version: Object,
24 | savings: Object,
25 | },
26 | });
27 | </script>
28 |
--------------------------------------------------------------------------------
/assets/js/components/Forecast/types.ts:
--------------------------------------------------------------------------------
1 | export function isForecastSlot(obj?: TimeseriesEntry | ForecastSlot): obj is ForecastSlot {
2 | return (obj as ForecastSlot).start !== undefined;
3 | }
4 |
5 | export interface TimeseriesEntry {
6 | val: number;
7 | ts: string;
8 | }
9 |
10 | export interface ForecastSlot {
11 | start: string;
12 | end: string;
13 | value: number;
14 | }
15 |
16 | export interface EnergyByDay {
17 | energy: number;
18 | complete: boolean;
19 | }
20 |
21 | export interface SolarDetails {
22 | scale?: number;
23 | today?: EnergyByDay;
24 | tomorrow?: EnergyByDay;
25 | dayAfterTomorrow?: EnergyByDay;
26 | timeseries?: TimeseriesEntry[];
27 | }
28 |
--------------------------------------------------------------------------------
/assets/js/components/Helper/IconSelectGroup.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <div class="root border d-flex gap-1" role="group">
3 | <slot></slot>
4 | </div>
5 | </template>
6 |
7 | <script lang="ts">
8 | import { defineComponent } from "vue";
9 |
10 | export default defineComponent({
11 | name: "IconSelectGroup",
12 | });
13 | </script>
14 |
15 | <style scoped>
16 | .root {
17 | border-radius: 20px;
18 | padding: 4px;
19 | }
20 | </style>
21 |
--------------------------------------------------------------------------------
/assets/js/components/Issue/types.d.ts:
--------------------------------------------------------------------------------
1 | export interface IssueData {
2 | title: string;
3 | description: string;
4 | steps: string;
5 | version: string;
6 | }
7 |
8 | export interface SectionData {
9 | included: boolean;
10 | content: string;
11 | }
12 |
13 | export interface Sections {
14 | yamlConfig: SectionData;
15 | uiConfig: SectionData;
16 | state: SectionData;
17 | logs: SectionData;
18 | }
19 |
20 | export interface GitHubContent {
21 | body: string;
22 | additional?: string;
23 | }
24 |
25 | // First level items are joint with empty line (\n\n), second level with line wrap (\n)
26 | export type Template = (string | string[])[];
27 |
28 | export type HelpType = "discussion" | "issue";
29 |
--------------------------------------------------------------------------------
/assets/js/components/Loadpoints/SettingsButton.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <button
3 | type="button"
4 | class="btn btn-sm btn-outline-secondary position-relative border-0 p-2 evcc-gray"
5 | data-testid="loadpoint-settings-button"
6 | >
7 | <shopicon-regular-adjust size="s"></shopicon-regular-adjust>
8 | </button>
9 | </template>
10 |
11 | <script lang="ts">
12 | import "@h2d2/shopicons/es/regular/adjust";
13 | import { defineComponent } from "vue";
14 |
15 | export default defineComponent({
16 | name: "LoadpointSettingsButton",
17 | props: {
18 | id: [String, Number],
19 | },
20 | });
21 | </script>
22 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Add.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path fill="currentColor" d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2z" />
4 | </svg>
5 | </template>
6 |
7 | <script lang="ts">
8 | import icon from "@/mixins/icon";
9 | import { defineComponent } from "vue";
10 |
11 | export default defineComponent({
12 | name: "Add",
13 | mixins: [icon],
14 | });
15 | </script>
16 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Dropdown.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M11.475 14.475L7.85 10.85q-.075-.075-.112-.162T7.7 10.5q0-.2.138-.35T8.2 10h7.6q.225 0 .363.15t.137.35q0 .05-.15.35l-3.625 3.625q-.125.125-.25.175T12 14.7t-.275-.05t-.25-.175"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | import icon from "@/mixins/icon";
13 |
14 | export default defineComponent({
15 | name: "Dropdown",
16 | mixins: [icon],
17 | });
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/DynamicPrice.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <g
4 | fill="none"
5 | stroke="currentColor"
6 | stroke-linecap="round"
7 | stroke-linejoin="round"
8 | stroke-width="2"
9 | >
10 | <circle cx="8" cy="8" r="6" />
11 | <path d="M18.09 10.37A6 6 0 1 1 10.34 18M7 6h1v4" />
12 | <path d="m16.71 13.88l.7.71l-2.82 2.82" />
13 | </g>
14 | </svg>
15 | </template>
16 |
17 | <script lang="ts">
18 | import { defineComponent } from "vue";
19 | import icon from "@/mixins/icon";
20 |
21 | export default defineComponent({
22 | name: "DynamicPrice",
23 | mixins: [icon],
24 | });
25 | </script>
26 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Edit.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M5 19h1.425L16.2 9.225L14.775 7.8L5 17.575zm-1 2q-.425 0-.712-.288T3 20v-2.425q0-.4.15-.763t.425-.637L16.2 3.575q.3-.275.663-.425t.762-.15t.775.15t.65.45L20.425 5q.3.275.437.65T21 6.4q0 .4-.138.763t-.437.662l-12.6 12.6q-.275.275-.638.425t-.762.15zM19 6.4L17.6 5zm-3.525 2.125l-.7-.725L16.2 9.225z"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | import icon from "@/mixins/icon";
13 |
14 | export default defineComponent({
15 | name: "Edit",
16 | mixins: [icon],
17 | });
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/PlanEnd.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M21 18q-.425 0-.712-.288T20 17V7q0-.425.288-.712T21 6t.713.288T22 7v10q0 .425-.288.713T21 18m-6.825-5H3q-.425 0-.712-.288T2 12t.288-.712T3 11h11.175L11.3 8.1q-.275-.275-.288-.687T11.3 6.7q.275-.275.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.687.275T11.3 17.3q-.3-.3-.3-.712t.3-.713z"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | import icon from "@/mixins/icon";
13 |
14 | export default defineComponent({
15 | name: "PlanEnd",
16 | mixins: [icon],
17 | });
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Play.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M8 17.175V6.825q0-.425.3-.713t.7-.287q.125 0 .263.037t.262.113l8.15 5.175q.225.15.338.375t.112.475t-.112.475t-.338.375l-8.15 5.175q-.125.075-.262.113T9 18.175q-.4 0-.7-.288t-.3-.712m2-1.825L15.25 12L10 8.65z"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | import icon from "@/mixins/icon";
13 |
14 | export default defineComponent({
15 | name: "Play",
16 | mixins: [icon],
17 | });
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Shm.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 32 32">
3 | <path
4 | d="M3.046,6.43c-1.365,-0 -2.513,1.149 -2.513,2.513l0,15.66c2.847,-1.757 15.172,-8.696 30.933,-8.411l0,-9.762l-28.42,-0Z"
5 | fill="currentColor"
6 | />
7 | <path
8 | d="M1.5,25.57l27.454,0c1.365,0 2.513,-1.149 2.513,-2.513l-0.001,-5.568c-14.933,-0.278 -26.74,6.137 -29.965,8.082"
9 | fill="currentColor"
10 | />
11 | </svg>
12 | </template>
13 |
14 | <script lang="ts">
15 | import { defineComponent } from "vue";
16 | import icon from "@/mixins/icon";
17 |
18 | export default defineComponent({
19 | name: "Shm",
20 | mixins: [icon],
21 | });
22 | </script>
23 |
--------------------------------------------------------------------------------
/assets/js/components/MaterialIcon/Total.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg :style="svgStyle" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M4.83 4.61A1 1 0 0 1 5.75 4h12.5a1 1 0 1 1 0 2H8.11l4.95 5.115a1 1 0 0 1 .04 1.346L7.924 18.5H18.25a1 1 0 1 1 0 2H5.75a1 1 0 0 1-.76-1.65l5.999-6.999L5.03 5.695a1 1 0 0 1-.2-1.085"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | import icon from "@/mixins/icon";
13 |
14 | export default defineComponent({
15 | name: "Total",
16 | mixins: [icon],
17 | });
18 | </script>
19 |
--------------------------------------------------------------------------------
/assets/js/components/MultiIcon/index.ts:
--------------------------------------------------------------------------------
1 | import MultiIcon from "./MultiIcon.vue";
2 |
3 | export default MultiIcon;
4 |
--------------------------------------------------------------------------------
/assets/js/components/Savings/LiveCommunity.stories.ts:
--------------------------------------------------------------------------------
1 | import LiveCommunity from "./LiveCommunity.vue";
2 | import type { Meta, StoryFn } from "@storybook/vue3";
3 |
4 | export default {
5 | title: "Savings/LiveCommunity",
6 | component: LiveCommunity,
7 | parameters: {
8 | layout: "centered",
9 | },
10 | } as Meta<typeof LiveCommunity>;
11 |
12 | const Template: StoryFn<typeof LiveCommunity> = () => ({
13 | components: { LiveCommunity },
14 | template: "<LiveCommunity />",
15 | });
16 |
17 | export const Default = Template.bind({});
18 |
--------------------------------------------------------------------------------
/assets/js/components/Savings/communityApi.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const api = axios.create({
4 | baseURL: "https://api.evcc.io/v1/",
5 | headers: {
6 | Accept: "application/json",
7 | },
8 | });
9 |
10 | // global error handling
11 | api.interceptors.response.use(
12 | (response) => response,
13 | (error) => {
14 | const url = error.config.baseURL + error.config.url;
15 | const message = `${error.message}: API request failed ${url}`;
16 | window.app.raise({ message });
17 | return Promise.reject(error);
18 | }
19 | );
20 |
21 | export default api;
22 |
--------------------------------------------------------------------------------
/assets/js/components/Savings/types.d.ts:
--------------------------------------------------------------------------------
1 | export interface LiveCommunityData {
2 | totalClients: number;
3 | activeClients: number;
4 | totalInstances: number;
5 | activeInstances: number;
6 | chargePower: number;
7 | greenPower: number;
8 | maxChargePower: number;
9 | maxGreenPower: number;
10 | chargeEnergy: number;
11 | greenEnergy: number;
12 | }
13 | export type Period = "30d" | "365d" | "thisYear" | "total";
14 |
--------------------------------------------------------------------------------
/assets/js/components/Site/types.d.ts:
--------------------------------------------------------------------------------
1 | export interface Grid {
2 | power?: number;
3 | }
4 |
--------------------------------------------------------------------------------
/assets/js/components/Top/baseapi.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const { protocol, hostname, port, pathname } = window.location;
4 |
5 | const baseAPI = axios.create({
6 | baseURL: protocol + "//" + hostname + (port ? ":" + port : "") + pathname,
7 | });
8 |
9 | // global error handling
10 | baseAPI.interceptors.response.use(
11 | (response) => response,
12 | (error) => {
13 | const url = error.config.baseURL + error.config.url;
14 | const message = `${error.message}: API request failed ${url}`;
15 | window.app.raise({ message });
16 | }
17 | );
18 |
19 | export default baseAPI;
20 |
--------------------------------------------------------------------------------
/assets/js/components/Top/types.d.ts:
--------------------------------------------------------------------------------
1 | export interface Provider {
2 | title: string;
3 | authenticated: boolean;
4 | loginPath: string;
5 | logoutPath: string;
6 | }
7 |
--------------------------------------------------------------------------------
/assets/js/components/VehicleIcon/Climate.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg width="1em" height="1em" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M20 12H4q-.825 0-1.412-.587T2 10V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v6q0 .825-.587 1.413T20 12M4 19v-2q1.25 0 2.125-.875T7 14h2q0 2.075-1.463 3.538T4 19m16 0q-2.075 0-3.537-1.463T15 14h2q0 1.25.875 2.125T20 17zm-9 1v-6h2v6zm9-10H4zM6 10V8q0-.825.588-1.412T8 6h8q.825 0 1.413.588T18 8v2h-2V8H8v2zm-2 0h16V4H4z"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 |
13 | export default defineComponent({
14 | name: "Climate",
15 | });
16 | </script>
17 |
--------------------------------------------------------------------------------
/assets/js/components/VehicleIcon/Desktop.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg width="1em" height="1em" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M10 19v-2H4q-.825 0-1.412-.587T2 15V5q0-.825.588-1.412T4 3h16q.825 0 1.413.588T22 5v10q0 .825-.587 1.413T20 17h-6v2h1q.425 0 .713.288T16 20t-.288.713T15 21H9q-.425 0-.712-.288T8 20t.288-.712T9 19zm-6-4h16V5H4zm0 0V5z"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | export default defineComponent({
13 | name: "Desktop",
14 | });
15 | </script>
16 |
--------------------------------------------------------------------------------
/assets/js/components/VehicleIcon/Floorlamp.vue:
--------------------------------------------------------------------------------
1 | <template>
2 | <svg width="1em" height="1em" viewBox="0 0 24 24">
3 | <path
4 | fill="currentColor"
5 | d="M12 19q-.425 0-.712-.288T11 18v-7H6q-.5 0-.8-.4t-.15-.9L7 3.4q.2-.625.725-1.013T8.9 2h6.2q.65 0 1.175.388T17 3.4l1.95 6.3q.15.5-.15.9t-.8.4h-5v7q0 .425-.288.713T12 19M7.35 9h9.3L15.1 4H8.9zM9 22q-.425 0-.712-.288T8 21t.288-.712T9 20h6q.425 0 .713.288T16 21t-.288.713T15 22zm3-15.5"
6 | />
7 | </svg>
8 | </template>
9 |
10 | <script lang="ts">
11 | import { defineComponent } from "vue";
12 | export default defineComponent({
13 | name: "Floorlamp",
14 | });
15 | </script>
16 |
--------------------------------------------------------------------------------
/assets/js/components/VehicleIcon/index.ts:
--------------------------------------------------------------------------------
1 | import VehicleIcon from "./VehicleIcon.vue";
2 |
3 | export default VehicleIcon;
4 |
--------------------------------------------------------------------------------
/assets/js/experimental.js:
--------------------------------------------------------------------------------
1 | import settings from "./settings";
2 |
3 | const KM = "km";
4 | const MILES = "mi";
5 | export const UNITS = [KM, MILES];
6 |
7 | const MILES_FACTOR = 0.6213711922;
8 |
9 | function isMiles() {
10 | return settings.unit === MILES;
11 | }
12 |
13 | export function distanceValue(value) {
14 | return isMiles() ? value * MILES_FACTOR : value;
15 | }
16 |
17 | export function distanceUnit() {
18 | return isMiles() ? "mi" : "km";
19 | }
20 |
21 | export function getUnits() {
22 | return isMiles() ? MILES : KM;
23 | }
24 |
25 | export function setUnits(value) {
26 | settings.unit = value;
27 | }
28 |
--------------------------------------------------------------------------------
/assets/js/featureflags.ts:
--------------------------------------------------------------------------------
1 | import type { App } from "vue";
2 | import settings from "./settings";
3 |
4 | export function setHiddenFeatures(value: boolean) {
5 | settings.hiddenFeatures = value;
6 | }
7 |
8 | export function getHiddenFeatures() {
9 | return settings.hiddenFeatures;
10 | }
11 |
12 | export default {
13 | install: (app: App) => {
14 | app.config.globalProperties.$hiddenFeatures = getHiddenFeatures;
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/assets/js/restart.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from "vue";
2 | import api from "./api";
3 |
4 | const restart = reactive({
5 | restartNeeded: false,
6 | restarting: false,
7 | });
8 |
9 | export async function performRestart() {
10 | try {
11 | await api.post("/system/shutdown");
12 | restart.restarting = true;
13 | } catch (e) {
14 | alert(`Unable to restart server. ${e}`);
15 | }
16 | }
17 |
18 | export function restartComplete() {
19 | restart.restarting = false;
20 | restart.restartNeeded = false;
21 | }
22 |
23 | export function showRestarting() {
24 | restart.restarting = true;
25 | }
26 |
27 | export default restart;
28 |
--------------------------------------------------------------------------------
/assets/js/types/shopicons.d.ts:
--------------------------------------------------------------------------------
1 | declare module "@h2d2/shopicons/es/regular/*";
2 | declare module "@h2d2/shopicons/es/filled/*";
3 |
--------------------------------------------------------------------------------
/assets/js/types/vue.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
2 | import type { ComponentCustomProperties } from "vue";
3 |
4 | declare module "vue" {
5 | interface ComponentCustomProperties {
6 | /**
7 | * Whether experimental UI features should be shown.
8 | */
9 | $hiddenFeatures: () => boolean;
10 | $refs: { [key: string]: HTMLElement | undefined };
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/assets/js/utils/convertRates.ts:
--------------------------------------------------------------------------------
1 | import type { Rate } from "../types/evcc";
2 | import type { ForecastSlot } from "../components/Forecast/types";
3 |
4 | function convertRate(slot: ForecastSlot): Rate {
5 | return {
6 | start: new Date(slot.start),
7 | end: new Date(slot.end),
8 | value: slot.value,
9 | };
10 | }
11 |
12 | export default function convertRates(slots: ForecastSlot[] | null): Rate[] {
13 | if (!slots) return [];
14 | return slots.map(convertRate);
15 | }
16 |
--------------------------------------------------------------------------------
/assets/js/utils/debounce.ts:
--------------------------------------------------------------------------------
1 | import type { Timeout } from "@/types/evcc";
2 |
3 | export function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): T {
4 | let timer: Timeout;
5 | return ((...args: any[]) => {
6 | if (timer) clearTimeout(timer);
7 | timer = setTimeout(() => fn(...args), delay);
8 | }) as T;
9 | }
10 |
--------------------------------------------------------------------------------
/assets/js/utils/deepClone.ts:
--------------------------------------------------------------------------------
1 | export default function <T>(obj: T): T {
2 | return JSON.parse(JSON.stringify(obj));
3 | }
4 |
--------------------------------------------------------------------------------
/assets/js/utils/deepEqual.ts:
--------------------------------------------------------------------------------
1 | export default function (obj1: any, obj2: any): boolean {
2 | return JSON.stringify(obj1) === JSON.stringify(obj2);
3 | }
4 |
--------------------------------------------------------------------------------
/assets/js/utils/fatal.ts:
--------------------------------------------------------------------------------
1 | import type { FatalError } from "@/types/evcc";
2 |
3 | const FATALS = ["configfile", "database"];
4 |
5 | function isError(fatal: FatalError[]) {
6 | return fatal.length > 0;
7 | }
8 |
9 | export function isUserConfigError(fatal: FatalError[]) {
10 | if (!isError(fatal)) {
11 | return false;
12 | }
13 |
14 | if (fatal.some((f) => FATALS.includes(f.class ?? ""))) {
15 | return false;
16 | }
17 |
18 | return true;
19 | }
20 |
21 | export function isSystemError(fatal: FatalError[]) {
22 | return isError(fatal) && !isUserConfigError(fatal);
23 | }
24 |
--------------------------------------------------------------------------------
/assets/js/utils/log.ts:
--------------------------------------------------------------------------------
1 | export const LOG_LEVELS = ["fatal", "error", "warn", "info", "debug", "trace"] as const;
2 | export const DEFAULT_LOG_LEVEL = "debug";
3 |
4 | export type LogLevel = (typeof LOG_LEVELS)[number];
5 |
--------------------------------------------------------------------------------
/assets/js/utils/native.ts:
--------------------------------------------------------------------------------
1 | export function isApp() {
2 | return navigator.userAgent.includes("evcc/");
3 | }
4 |
5 | export function appDetection() {
6 | if (isApp()) {
7 | const $html = document.querySelector("html");
8 | $html?.classList.add("app");
9 | }
10 | }
11 |
12 | export function sendToApp(data: { type: string }) {
13 | window.ReactNativeWebView?.postMessage(JSON.stringify(data));
14 | }
15 |
--------------------------------------------------------------------------------
/assets/js/utils/sleep.ts:
--------------------------------------------------------------------------------
1 | export default (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
2 |
--------------------------------------------------------------------------------
/assets/js/utils/useDebouncedComputed.ts:
--------------------------------------------------------------------------------
1 | import { ref, watch, type Ref } from "vue";
2 | import { debounce } from "./debounce";
3 |
4 | export function useDebouncedComputed<T>(
5 | getter: () => T,
6 | deps: () => any,
7 | delay: number = 100
8 | ): Ref<T> {
9 | const result = ref<T>();
10 | const debouncedUpdate = debounce(() => {
11 | result.value = getter();
12 | }, delay);
13 |
14 | watch(deps, debouncedUpdate, { immediate: true, deep: true });
15 |
16 | return result as Ref<T>;
17 | }
18 |
--------------------------------------------------------------------------------
/assets/public/meta/android-chrome-192x192-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/android-chrome-192x192-maskable.png
--------------------------------------------------------------------------------
/assets/public/meta/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/android-chrome-192x192.png
--------------------------------------------------------------------------------
/assets/public/meta/android-chrome-512x512-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/android-chrome-512x512-maskable.png
--------------------------------------------------------------------------------
/assets/public/meta/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/android-chrome-512x512.png
--------------------------------------------------------------------------------
/assets/public/meta/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/apple-touch-icon.png
--------------------------------------------------------------------------------
/assets/public/meta/browserconfig.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <browserconfig>
3 | <msapplication>
4 | <tile>
5 | <square70x70logo src="meta/mstile-70x70.png" />
6 | <square150x150logo src="meta/mstile-150x150.png" />
7 | <square310x310logo src="meta/mstile-310x310.png" />
8 | <TileColor>#1C2445</TileColor>
9 | </tile>
10 | </msapplication>
11 | </browserconfig>
--------------------------------------------------------------------------------
/assets/public/meta/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/favicon-16x16.png
--------------------------------------------------------------------------------
/assets/public/meta/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/public/meta/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/favicon.ico
--------------------------------------------------------------------------------
/assets/public/meta/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/mstile-144x144.png
--------------------------------------------------------------------------------
/assets/public/meta/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/mstile-150x150.png
--------------------------------------------------------------------------------
/assets/public/meta/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/mstile-310x150.png
--------------------------------------------------------------------------------
/assets/public/meta/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/mstile-310x310.png
--------------------------------------------------------------------------------
/assets/public/meta/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/assets/public/meta/mstile-70x70.png
--------------------------------------------------------------------------------
/charger/config.go:
--------------------------------------------------------------------------------
1 | package charger
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/charger/config"
8 | )
9 |
10 | var registry = config.Registry
11 |
12 | // Types returns the list of types
13 | func Types() []string {
14 | return registry.Types()
15 | }
16 |
17 | // NewFromConfig creates charger from configuration
18 | func NewFromConfig(ctx context.Context, typ string, other map[string]any) (api.Charger, error) {
19 | return config.NewFromConfig(ctx, typ, other)
20 | }
21 |
--------------------------------------------------------------------------------
/charger/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/evcc-io/evcc/api"
9 | reg "github.com/evcc-io/evcc/util/registry"
10 | )
11 |
12 | var Registry = reg.New[api.Charger]("charger")
13 |
14 | // NewFromConfig creates charger from configuration
15 | func NewFromConfig(ctx context.Context, typ string, other map[string]any) (api.Charger, error) {
16 | factory, err := Registry.Get(strings.ToLower(typ))
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | v, err := factory(ctx, other)
22 | if err != nil {
23 | return nil, fmt.Errorf("cannot create charger type '%s': %w", typ, err)
24 | }
25 |
26 | return v, nil
27 | }
28 |
--------------------------------------------------------------------------------
/charger/connectiq/types.go:
--------------------------------------------------------------------------------
1 | package connectiq
2 |
3 | type ChargeStatus struct {
4 | Amps int64 `json:"amps"`
5 | Pp int64 `json:"pp"`
6 | Status string `json:"status"`
7 | Std int64 `json:"std"`
8 | }
9 |
10 | type ChargeMaxAmps struct {
11 | Max int64 `json:"max"`
12 | }
13 |
14 | type MeterStatus struct {
15 | App []float64 `json:"app"`
16 | Curr []float64 `json:"curr"`
17 | Fac []float64 `json:"fac"`
18 | Pow []float64 `json:"pow"`
19 | Volt []float64 `json:"volt"`
20 | }
21 |
22 | type MeterRead struct {
23 | Energy float64 `json:"energy"`
24 | }
25 |
--------------------------------------------------------------------------------
/charger/echarge/types.go:
--------------------------------------------------------------------------------
1 | package echarge
2 |
3 | const (
4 | ModeEco = "eco"
5 | ModeManual = "manual"
6 | )
7 |
--------------------------------------------------------------------------------
/charger/embed.go:
--------------------------------------------------------------------------------
1 | package charger
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | )
6 |
7 | type embed struct {
8 | Icon_ string `mapstructure:"icon"`
9 | Features_ []api.Feature `mapstructure:"features"`
10 | }
11 |
12 | var _ api.IconDescriber = (*embed)(nil)
13 |
14 | // Icon implements the api.IconDescriber interface
15 | func (v *embed) Icon() string {
16 | return v.Icon_
17 | }
18 |
19 | var _ api.FeatureDescriber = (*embed)(nil)
20 |
21 | // Features implements the api.FeatureDescriber interface
22 | func (v *embed) Features() []api.Feature {
23 | return v.Features_
24 | }
25 |
--------------------------------------------------------------------------------
/charger/measurement/energy.go:
--------------------------------------------------------------------------------
1 | package measurement
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/evcc-io/evcc/plugin"
8 | )
9 |
10 | type Energy struct {
11 | Power *plugin.Config // optional
12 | Energy *plugin.Config // optional
13 | }
14 |
15 | func (cc *Energy) Configure(ctx context.Context) (
16 | func() (float64, error),
17 | func() (float64, error),
18 | error,
19 | ) {
20 | powerG, err := cc.Power.FloatGetter(ctx)
21 | if err != nil {
22 | return nil, nil, fmt.Errorf("power: %w", err)
23 | }
24 |
25 | energyG, err := cc.Energy.FloatGetter(ctx)
26 | if err != nil {
27 | return nil, nil, fmt.Errorf("energy: %w", err)
28 | }
29 |
30 | return powerG, energyG, nil
31 | }
32 |
--------------------------------------------------------------------------------
/charger/nrgble.go:
--------------------------------------------------------------------------------
1 | //go:build !linux
2 |
3 | package charger
4 |
5 | import (
6 | "errors"
7 |
8 | "github.com/evcc-io/evcc/api"
9 | )
10 |
11 | func init() {
12 | registry.Add("nrgkick-bluetooth", NewNRGKickBLEFromConfig)
13 | }
14 |
15 | // NewNRGKickBLEFromConfig creates a NRGKickBLE charger from generic config
16 | func NewNRGKickBLEFromConfig(other map[string]any) (api.Charger, error) {
17 | return nil, errors.New("NRGKick bluetooth is only supported on linux")
18 | }
19 |
--------------------------------------------------------------------------------
/charger/ocpp/helper_test.go:
--------------------------------------------------------------------------------
1 | package ocpp
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | "github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestSortByAge(t *testing.T) {
12 | assert.Equal(t, []types.MeterValue{
13 | {Timestamp: nil},
14 | {Timestamp: types.NewDateTime(time.UnixMilli(1))},
15 | {Timestamp: types.NewDateTime(time.UnixMilli(2))},
16 | {Timestamp: types.NewDateTime(time.UnixMilli(3))},
17 | }, sortByAge([]types.MeterValue{
18 | {Timestamp: types.NewDateTime(time.UnixMilli(3))},
19 | {Timestamp: types.NewDateTime(time.UnixMilli(1))},
20 | {Timestamp: nil},
21 | {Timestamp: types.NewDateTime(time.UnixMilli(2))},
22 | }))
23 | }
24 |
--------------------------------------------------------------------------------
/charger/template.go:
--------------------------------------------------------------------------------
1 | package charger
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/util/templates"
8 | )
9 |
10 | func init() {
11 | registry.AddCtx("template", NewChargerFromTemplateConfig)
12 | }
13 |
14 | func NewChargerFromTemplateConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
15 | instance, err := templates.RenderInstance(templates.Charger, other)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | return NewFromConfig(ctx, instance.Type, instance.Other)
21 | }
22 |
--------------------------------------------------------------------------------
/charger/warp/const.go:
--------------------------------------------------------------------------------
1 | package warp
2 |
3 | import "time"
4 |
5 | const (
6 | RootTopic = "warp"
7 | Timeout = 30 * time.Second
8 | )
9 |
--------------------------------------------------------------------------------
/cmd/cache.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | )
6 |
7 | // cacheCmd represents the cache command
8 | var cacheCmd = &cobra.Command{
9 | Use: "cache",
10 | Short: "Manage cache entries",
11 | }
12 |
13 | func init() {
14 | rootCmd.AddCommand(cacheCmd)
15 | }
16 |
--------------------------------------------------------------------------------
/cmd/demo.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | _ "embed" // for yaml
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/evcc-io/evcc/api/globalconfig"
9 | )
10 |
11 | //go:embed demo.yaml
12 | var demoYaml string
13 |
14 | func demoConfig(conf *globalconfig.All) error {
15 | viper.SetConfigType("yaml")
16 | if err := viper.ReadConfig(strings.NewReader(demoYaml)); err != nil {
17 | return fmt.Errorf("failed decoding demo config: %w", err)
18 | }
19 |
20 | if err := viper.UnmarshalExact(conf); err != nil {
21 | return fmt.Errorf("failed loading demo config: %w", err)
22 | }
23 |
24 | // parse log levels after reading config
25 | parseLogLevels()
26 |
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/detect/analyze.go:
--------------------------------------------------------------------------------
1 | package detect
2 |
3 | import "github.com/evcc-io/evcc/cmd/detect/tasks"
4 |
5 | type Criteria map[string]any
6 |
7 | type TypeSummary struct {
8 | Results []tasks.Result
9 | Found, Unique bool
10 | }
11 |
12 | type Summary struct {
13 | Charger, Grid, PV, Charge, Battery, Meter TypeSummary
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/detect/tasks/const.go:
--------------------------------------------------------------------------------
1 | package tasks
2 |
3 | import "time"
4 |
5 | const timeout = 200 * time.Millisecond
6 |
--------------------------------------------------------------------------------
/cmd/discuss.tpl:
--------------------------------------------------------------------------------
1 | <!-- Detaillierte Problembeschreibung bitte hier -->
2 |
3 |
4 |
5 | {{ if .CfgError -}}
6 | Fehlermeldung:
7 |
8 | ```
9 | {{ .CfgError }}
10 | ```
11 |
12 | {{ end -}}
13 |
14 | {{ if .CfgContent -}}
15 | <details><summary>Konfiguration{{ if .CfgFile }} ({{ .CfgFile }}){{ end }}</summary>
16 |
17 | ```yaml
18 | {{ .CfgContent }}
19 | ```
20 |
21 | </details>
22 | {{ end -}}
23 |
24 | {{ if .Version -}}
25 | Version: `{{ .Version }}`
26 | {{ end -}}
27 |
--------------------------------------------------------------------------------
/cmd/dump.tpl:
--------------------------------------------------------------------------------
1 |
2 | {{ if .CfgError -}}
3 | Fehlermeldung:
4 |
5 | {{ .CfgError | indent 4 }}
6 |
7 | {{ end -}}
8 |
9 | {{ if .CfgContent -}}
10 | Konfiguration{{ if .CfgFile }} ({{ .CfgFile }}){{ end }}:
11 |
12 | {{ .CfgContent }}
13 |
14 | {{ end -}}
15 |
16 | {{ if .Version -}}
17 | Version: `{{ .Version }}`
18 | {{ end -}}
19 |
--------------------------------------------------------------------------------
/cmd/error_test.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "errors"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestError(t *testing.T) {
11 | res := &ClassError{
12 | ClassMeter,
13 | &DeviceError{
14 | "0815",
15 | errors.New("foo"),
16 | },
17 | }
18 |
19 | b, err := res.MarshalJSON()
20 | require.NoError(t, err)
21 | require.Equal(t, `{"class":"meter","device":"0815","error":"[0815] foo"}`, string(b))
22 | }
23 |
--------------------------------------------------------------------------------
/cmd/openapi/openapi.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "os"
7 |
8 | "github.com/getkin/kin-openapi/openapi3"
9 | )
10 |
11 | func main() {
12 | doc, err := openapi3.NewLoader().LoadFromFile(os.Args[1])
13 | if err != nil {
14 | log.Fatal("failed to load OpenAPI spec:", err)
15 | }
16 |
17 | // omit servers
18 | doc.Servers = nil
19 |
20 | b, err := json.MarshalIndent(doc, "", " ")
21 | if err != nil {
22 | log.Fatal(err)
23 | }
24 |
25 | if err := os.WriteFile(os.Args[2], b, 0o644); err != nil {
26 | log.Fatal(err)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/password.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | )
6 |
7 | var passwordCmd = &cobra.Command{
8 | Use: "password",
9 | Short: "Password administration",
10 | }
11 |
12 | func init() {
13 | rootCmd.AddCommand(passwordCmd)
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/settings.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | )
6 |
7 | // settingsCmd represents the configure command
8 | var settingsCmd = &cobra.Command{
9 | Use: "settings",
10 | Short: "Manage configuration settings",
11 | }
12 |
13 | func init() {
14 | rootCmd.AddCommand(settingsCmd)
15 | }
16 |
--------------------------------------------------------------------------------
/cmd/shutdown/shutdown.go:
--------------------------------------------------------------------------------
1 | package shutdown
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var (
8 | mu sync.Mutex
9 | handlers = make([]func(), 0)
10 | )
11 |
12 | // Register registers a function for executing on application shutdown
13 | func Register(cb func()) {
14 | mu.Lock()
15 | handlers = append(handlers, cb)
16 | mu.Unlock()
17 | }
18 |
19 | // Cleanup executes the registered shutdown functions when the stop channel closes
20 | func Cleanup(doneC chan struct{}) {
21 | var wg sync.WaitGroup
22 |
23 | mu.Lock()
24 | for _, cb := range handlers {
25 | wg.Go(cb)
26 | }
27 | mu.Unlock()
28 |
29 | wg.Wait()
30 | close(doneC)
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/sponsor.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/evcc-io/evcc/util/sponsor"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | // sponsorCmd represents the vehicle command
9 | var sponsorCmd = &cobra.Command{
10 | Use: "sponsor [name]",
11 | Short: "Validate sponsor token",
12 | Args: cobra.ExactArgs(1),
13 | Run: runSponsor,
14 | }
15 |
16 | func init() {
17 | rootCmd.AddCommand(sponsorCmd)
18 | }
19 |
20 | func runSponsor(cmd *cobra.Command, args []string) {
21 | token := args[0]
22 |
23 | if err := sponsor.ConfigureSponsorship(token); err != nil {
24 | fatal(err)
25 | }
26 |
27 | log.INFO.Println("sponsorship validated")
28 | }
29 |
--------------------------------------------------------------------------------
/core/circuit/config.go:
--------------------------------------------------------------------------------
1 | package circuit
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | "github.com/evcc-io/evcc/util/config"
6 | )
7 |
8 | func Root() api.Circuit {
9 | for _, dev := range config.Circuits().Devices() {
10 | if c := dev.Instance(); c.GetParent() == nil {
11 | return c
12 | }
13 | }
14 | return nil
15 | }
16 |
--------------------------------------------------------------------------------
/core/coordinator/dummy.go:
--------------------------------------------------------------------------------
1 | package coordinator
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | "github.com/evcc-io/evcc/core/loadpoint"
6 | )
7 |
8 | type dummy struct{}
9 |
10 | // NewDummy creates a dummy coordinator without vehicles
11 | func NewDummy() API {
12 | return new(dummy)
13 | }
14 |
15 | func (a *dummy) GetVehicles(_ bool) []api.Vehicle {
16 | return nil
17 | }
18 |
19 | func (a *dummy) Owner(api.Vehicle) loadpoint.API {
20 | return nil
21 | }
22 |
23 | func (a *dummy) Acquire(api.Vehicle) {}
24 |
25 | func (a *dummy) Release(api.Vehicle) {}
26 |
27 | func (a *dummy) IdentifyVehicleByStatus() api.Vehicle {
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/core/keys/auth.go:
--------------------------------------------------------------------------------
1 | package keys
2 |
3 | const (
4 | AdminPassword = "adminPassword"
5 | JwtSecret = "jwtSecretKey"
6 | )
7 |
--------------------------------------------------------------------------------
/core/loadpoint/error.go:
--------------------------------------------------------------------------------
1 | package loadpoint
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | )
8 |
9 | func AcceptableError(err error) bool {
10 | for _, e := range []error{api.ErrAsleep, api.ErrMustRetry, api.ErrNotAvailable} {
11 | if errors.Is(err, e) {
12 | return true
13 | }
14 | }
15 | return false
16 | }
17 |
--------------------------------------------------------------------------------
/core/metrics/types.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "database/sql"
5 | "errors"
6 | "time"
7 | )
8 |
9 | type SqlTime time.Time
10 |
11 | var _ sql.Scanner = (*SqlTime)(nil)
12 |
13 | func (st *SqlTime) Scan(value any) error {
14 | switch v := value.(type) {
15 | case time.Time:
16 | *st = SqlTime(v)
17 | case int64:
18 | *st = SqlTime(time.Unix(v, 0))
19 | case string:
20 | t, err := time.Parse(time.DateTime+"-07:00", v)
21 | if err == nil {
22 | *st = SqlTime(t)
23 | }
24 | return err
25 | default:
26 | return errors.New("unsupported timestamp type")
27 | }
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/core/planner/sort.go:
--------------------------------------------------------------------------------
1 | package planner
2 |
3 | import "github.com/evcc-io/evcc/api"
4 |
5 | // sortByCost is a sortFunc for slices.Sort
6 | func sortByCost(i, j api.Rate) int {
7 | switch {
8 | case i.Value < j.Value:
9 | return -1
10 | case i.Value > j.Value:
11 | return +1
12 | default:
13 | return j.Start.Compare(i.Start)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/progress.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | type Progress struct {
4 | min, step, current float64
5 | }
6 |
7 | func NewProgress(min, step float64) *Progress {
8 | return &Progress{
9 | min: min,
10 | step: step,
11 | current: min,
12 | }
13 | }
14 |
15 | func (p *Progress) NextStep(value float64) bool {
16 | // test guard
17 | if p != nil && value >= p.current {
18 | for p.current <= value {
19 | p.current += p.step
20 | }
21 |
22 | return true
23 | }
24 |
25 | return false
26 | }
27 |
28 | func (p *Progress) Reset() {
29 | // test guard
30 | if p != nil {
31 | p.current = p.min
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/progress_test.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestProgress(t *testing.T) {
11 | p := NewProgress(0, 10)
12 |
13 | tc := []struct {
14 | value float64
15 | res bool
16 | }{
17 | {-1, false},
18 | {0, true},
19 | {1, false},
20 | {5, false},
21 | {10, true},
22 | {15, false},
23 | {25, true},
24 | {30, true},
25 | {60, true},
26 | {65, false},
27 | {70, true},
28 | }
29 |
30 | for _, tc := range tc {
31 | require.Equal(t, tc.res, p.NextStep(tc.value), fmt.Sprintf("%.0f%%", tc.value))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/session/format_test.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | "golang.org/x/text/language"
8 | "golang.org/x/text/message"
9 | )
10 |
11 | func TestFormatValue(t *testing.T) {
12 | mp := message.NewPrinter(language.Make("en"))
13 |
14 | f := 1.2345
15 | assert.Equal(t, "1.234", formatValue(mp, f, 3))
16 | assert.Equal(t, "1.234", formatValue(mp, &f, 3))
17 | }
18 |
--------------------------------------------------------------------------------
/core/settings/settings.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | import "time"
4 |
5 | type Settings interface {
6 | SetString(key string, val string)
7 | SetInt(key string, val int64)
8 | SetFloat(key string, val float64)
9 | SetFloatPtr(key string, val *float64)
10 | SetTime(key string, val time.Time)
11 | SetJson(key string, val any) error
12 | SetBool(key string, val bool)
13 | String(key string) (string, error)
14 | Int(key string) (int64, error)
15 | Float(key string) (float64, error)
16 | Time(key string) (time.Time, error)
17 | Bool(key string) (bool, error)
18 | Json(key string, res any) error
19 | }
20 |
--------------------------------------------------------------------------------
/core/site/vehicles.go:
--------------------------------------------------------------------------------
1 | package site
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | "github.com/evcc-io/evcc/core/vehicle"
6 | )
7 |
8 | type Vehicles interface {
9 | // Settings returns the list of vehicle adapters
10 | Settings() []vehicle.API
11 |
12 | // ByName returns a single vehicle adapter by name
13 | ByName(string) (vehicle.API, error)
14 |
15 | // All returns the list of vehicle instances
16 | Instances() []api.Vehicle
17 | }
18 |
--------------------------------------------------------------------------------
/core/soc/helper.go:
--------------------------------------------------------------------------------
1 | package soc
2 |
3 | import "fmt"
4 |
5 | // Guard checks soc value for validity
6 | func Guard(soc float64, err error) (float64, error) {
7 | switch {
8 | case err != nil:
9 | return soc, err
10 |
11 | case soc < 0:
12 | return 0, fmt.Errorf("invalid soc: %.1f", soc)
13 |
14 | case soc > 100:
15 | return 100, fmt.Errorf("invalid soc: %.1f", soc)
16 |
17 | default:
18 | return soc, nil
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/wrapper/chargemeter.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | // ChargeMeter is a replacement for a physical charge meter.
8 | // It uses the charger's actual or max current to calculate power consumption.
9 | type ChargeMeter struct {
10 | sync.Mutex
11 | power float64
12 | }
13 |
14 | // SetPower updates meter's current power
15 | func (m *ChargeMeter) SetPower(power float64) {
16 | m.Lock()
17 | defer m.Unlock()
18 | m.power = power
19 | }
20 |
21 | // CurrentPower implements the api.Meter interface
22 | func (m *ChargeMeter) CurrentPower() (float64, error) {
23 | m.Lock()
24 | defer m.Unlock()
25 | return m.power, nil
26 | }
27 |
--------------------------------------------------------------------------------
/core/wrapper/chargemeter_test.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import (
4 | "testing"
5 |
6 | "go.uber.org/mock/gomock"
7 | )
8 |
9 | func TestProxyChargeMeter(t *testing.T) {
10 | ctrl := gomock.NewController(t)
11 | defer ctrl.Finish()
12 |
13 | tc := []float64{600, 1000, 2000}
14 | m := ChargeMeter{}
15 |
16 | for _, f := range tc {
17 | m.SetPower(f)
18 |
19 | if p, err := m.CurrentPower(); p != f || err != nil {
20 | t.Errorf("power: %.1f %v", p, err)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/wrapper/chargetimer_test.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | "github.com/benbjohnson/clock"
8 | )
9 |
10 | func TestTimer(t *testing.T) {
11 | ct := NewChargeTimer()
12 | clck := clock.NewMock()
13 | ct.clck = clck
14 |
15 | ct.StartCharge(false)
16 | clck.Add(time.Hour)
17 | ct.StopCharge()
18 | clck.Add(time.Hour)
19 |
20 | if d, err := ct.ChargeDuration(); d != 1*time.Hour || err != nil {
21 | t.Error(d, err)
22 | }
23 |
24 | // continue
25 | ct.StartCharge(true)
26 | clck.Add(2 * time.Hour)
27 | ct.StopCharge()
28 |
29 | if d, err := ct.ChargeDuration(); d != 3*time.Hour || err != nil {
30 | t.Error(d, err)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | /// <reference types="vite/client" />
2 |
--------------------------------------------------------------------------------
/hems/eebus/types.go:
--------------------------------------------------------------------------------
1 | package eebus
2 |
3 | type status int
4 |
5 | const (
6 | StatusUnlimited status = iota
7 | StatusLimited
8 | StatusFailsafe
9 | )
10 |
--------------------------------------------------------------------------------
/i18n/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "jsonRecursiveSort": true,
3 | "plugins": ["prettier-plugin-sort-json"]
4 | }
5 |
--------------------------------------------------------------------------------
/i18n/et.json:
--------------------------------------------------------------------------------
1 | {
2 | "batterySettings": {
3 | "batteryLevel": "Akutase",
4 | "capacity": "{energy} / {total}",
5 | "control": "Akuhaldus"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/icon.png
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { Config } from "@jest/types";
2 |
3 | export default {
4 | preset: "@vue/cli-plugin-unit-jest/presets/no-babel",
5 | testMatch: ["**/*.spec.ts"],
6 | } satisfies Config.InitialOptions;
7 |
--------------------------------------------------------------------------------
/meter/bosch/types.go:
--------------------------------------------------------------------------------
1 | package bosch
2 |
3 | type LoginResponse struct {
4 | wuSid string
5 | }
6 |
7 | type StatusResponse struct {
8 | CurrentBatterySoc float64
9 | SellToGrid float64
10 | BuyFromGrid float64
11 | PvPower float64
12 | BatteryChargePower float64
13 | BatteryDischargePower float64
14 | }
15 |
--------------------------------------------------------------------------------
/meter/config.go:
--------------------------------------------------------------------------------
1 | package meter
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/meter/config"
8 | )
9 |
10 | var registry = config.Registry
11 |
12 | // Types returns the list of types
13 | func Types() []string {
14 | return registry.Types()
15 | }
16 |
17 | // NewFromConfig creates meter from configuration
18 | func NewFromConfig(ctx context.Context, typ string, other map[string]any) (api.Meter, error) {
19 | return config.NewFromConfig(ctx, typ, other)
20 | }
21 |
--------------------------------------------------------------------------------
/meter/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/evcc-io/evcc/api"
9 | reg "github.com/evcc-io/evcc/util/registry"
10 | )
11 |
12 | var Registry = reg.New[api.Meter]("meter")
13 |
14 | // NewFromConfig creates meter from configuration
15 | func NewFromConfig(ctx context.Context, typ string, other map[string]any) (api.Meter, error) {
16 | factory, err := Registry.Get(strings.ToLower(typ))
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | v, err := factory(ctx, other)
22 | if err != nil {
23 | return nil, fmt.Errorf("cannot create meter type '%s': %w", typ, err)
24 | }
25 |
26 | return v, nil
27 | }
28 |
--------------------------------------------------------------------------------
/meter/discovergy/types.go:
--------------------------------------------------------------------------------
1 | package discovergy
2 |
3 | const API = "https://api.inexogy.com/public/v1"
4 |
5 | type Meter struct {
6 | MeterID string `json:"meterId"`
7 | SerialNumber string `json:"serialNumber"`
8 | FullSerialNumber string `json:"fullSerialNumber"`
9 | }
10 |
11 | type Reading struct {
12 | Time int64
13 | Values struct {
14 | EnergyOut int64
15 | Energy1, Energy2 int64
16 | Voltage1, Voltage2, Voltage3 int64
17 | EnergyOut1, EnergyOut2 int64
18 | Power1, Power2, Power3 int64
19 | Power int64
20 | Energy int64
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/meter/goodwe/types.go:
--------------------------------------------------------------------------------
1 | package goodwe
2 |
3 | import (
4 | "net"
5 |
6 | "github.com/evcc-io/evcc/util"
7 | )
8 |
9 | type Server struct {
10 | log *util.Logger
11 | conn *net.UDPConn
12 | inverters map[string]*util.Monitor[Inverter]
13 | }
14 |
15 | type Inverter struct {
16 | PvPower float64
17 | NetPower float64
18 | BatteryPower float64
19 | Soc float64
20 | }
21 |
--------------------------------------------------------------------------------
/meter/measurement/energy.go:
--------------------------------------------------------------------------------
1 | package measurement
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/evcc-io/evcc/plugin"
8 | )
9 |
10 | type Energy struct {
11 | Power plugin.Config
12 | Energy *plugin.Config // optional
13 | }
14 |
15 | func (cc *Energy) Configure(ctx context.Context) (
16 | func() (float64, error),
17 | func() (float64, error),
18 | error,
19 | ) {
20 | powerG, err := cc.Power.FloatGetter(ctx)
21 | if err != nil {
22 | return nil, nil, fmt.Errorf("power: %w", err)
23 | }
24 |
25 | energyG, err := cc.Energy.FloatGetter(ctx)
26 | if err != nil {
27 | return nil, nil, fmt.Errorf("energy: %w", err)
28 | }
29 |
30 | return powerG, energyG, nil
31 | }
32 |
--------------------------------------------------------------------------------
/meter/mystrom.go:
--------------------------------------------------------------------------------
1 | package meter
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | "github.com/evcc-io/evcc/meter/mystrom"
6 | "github.com/evcc-io/evcc/util"
7 | )
8 |
9 | // myStrom switch:
10 | // https://api.mystrom.ch/#fbb2c698-e37a-4584-9324-3f8b2f615fe2
11 |
12 | func init() {
13 | registry.Add("mystrom", NewMyStromFromConfig)
14 | }
15 |
16 | // NewMyStromFromConfig creates a myStrom meter from generic config
17 | func NewMyStromFromConfig(other map[string]any) (api.Meter, error) {
18 | var cc struct {
19 | URI string
20 | }
21 |
22 | if err := util.DecodeOther(other, &cc); err != nil {
23 | return nil, err
24 | }
25 |
26 | return mystrom.NewConnection(cc.URI), nil
27 | }
28 |
--------------------------------------------------------------------------------
/meter/obis/obis.go:
--------------------------------------------------------------------------------
1 | package obis
2 |
3 | // https://www.kbr.de/de/obis-kennzeichen/elektrizitaet
4 |
5 | const (
6 | PowerConsumption = "1-0:1.4.0"
7 | EnergyConsumption = "1-0:1.8.0"
8 | PowerFeedIn = "1-0:2.4.0"
9 | EnergyFeedIn = "1-0:2.8.0"
10 |
11 | PowerConsumptionL1 = "1-0:21.4.0"
12 | EnergyConsumptionL1 = "1-0:21.8.0"
13 | CurrentL1 = "1-0:31.4.0"
14 |
15 | PowerConsumptionL2 = "1-0:41.4.0"
16 | EnergyConsumptionL2 = "1-0:41.8.0"
17 | CurrentL2 = "1-0:51.4.0"
18 |
19 | PowerConsumptionL3 = "1-0:61.4.0"
20 | EnergyConsumptionL3 = "1-0:61.8.0"
21 | CurrentL3 = "1-0:71.4.0"
22 | )
23 |
--------------------------------------------------------------------------------
/meter/shelly/types.go:
--------------------------------------------------------------------------------
1 | package shelly
2 |
3 | // DeviceInfo is the common /shelly endpoint response
4 | // https://shelly-api-docs.shelly.cloud/gen1/#shelly
5 | // https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Shelly#http-endpoint-shelly
6 | type DeviceInfo struct {
7 | Mac string `json:"mac"`
8 | Gen int `json:"gen"`
9 | Model string `json:"model"`
10 | Type string `json:"type"`
11 | Auth bool `json:"auth"`
12 | AuthEn bool `json:"auth_en"`
13 | NumMeters int `json:"num_meters"`
14 | Profile string `json:"profile"`
15 | }
16 |
--------------------------------------------------------------------------------
/meter/template.go:
--------------------------------------------------------------------------------
1 | package meter
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/util/templates"
8 | )
9 |
10 | func init() {
11 | registry.AddCtx("template", NewMeterFromTemplateConfig)
12 | }
13 |
14 | func NewMeterFromTemplateConfig(ctx context.Context, other map[string]any) (api.Meter, error) {
15 | instance, err := templates.RenderInstance(templates.Meter, other)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | return NewFromConfig(ctx, instance.Type, instance.Other)
21 | }
22 |
--------------------------------------------------------------------------------
/meter/tplink.go:
--------------------------------------------------------------------------------
1 | package meter
2 |
3 | import (
4 | "github.com/evcc-io/evcc/api"
5 | "github.com/evcc-io/evcc/meter/tplink"
6 | "github.com/evcc-io/evcc/util"
7 | )
8 |
9 | func init() {
10 | registry.Add("tplink", NewTPLinkFromConfig)
11 | }
12 |
13 | // NewTPLinkFromConfig creates a tapo meter from generic config
14 | func NewTPLinkFromConfig(other map[string]any) (api.Meter, error) {
15 | var cc struct {
16 | URI string
17 | }
18 |
19 | if err := util.DecodeOther(other, &cc); err != nil {
20 | return nil, err
21 | }
22 |
23 | return tplink.NewConnection(cc.URI)
24 | }
25 |
--------------------------------------------------------------------------------
/meter/usage_pv.go:
--------------------------------------------------------------------------------
1 | package meter
2 |
3 | type pvMaxACPower struct {
4 | MaxACPower float64
5 | }
6 |
7 | // var _ api.MaxACPowerGetter = (*pvMaxACPower)(nil)
8 |
9 | // Decorator returns the max AC power decorator
10 | func (m *pvMaxACPower) Decorator() func() float64 {
11 | if m.MaxACPower == 0 {
12 | return nil
13 | }
14 | return func() float64 {
15 | return m.MaxACPower
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packaging/init/evcc.service:
--------------------------------------------------------------------------------
1 | # evcc.service
2 | #
3 |
4 | [Unit]
5 | Description=evcc
6 | Requires=network-online.target
7 | After=syslog.target network.target network-online.target
8 | Wants=network-online.target
9 | StartLimitIntervalSec=10
10 | StartLimitBurst=10
11 |
12 | [Service]
13 | AmbientCapabilities=CAP_NET_BIND_SERVICE
14 | ExecStart=/usr/bin/evcc
15 | Environment="EVCC_DATABASE_DSN=/var/lib/evcc/evcc.db"
16 | Restart=always
17 | RestartSec=10
18 |
19 | User=evcc
20 | Group=evcc
21 |
22 | [Install]
23 | WantedBy=multi-user.target
24 |
--------------------------------------------------------------------------------
/packaging/patch/asn1.diff:
--------------------------------------------------------------------------------
1 | --- asn1.go 2022-09-15 13:21:17.000000000 +0200
2 | +++ asn1_new.go 2022-09-15 13:22:12.000000000 +0200
3 | @@ -255,7 +255,7 @@
4 | switch bytes[0] {
5 | case 0:
6 | *out = false
7 | - case 0xff:
8 | + case 1, 0xff:
9 | *out = true
10 | default:
11 | return false
12 |
--------------------------------------------------------------------------------
/packaging/scripts/preremove.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
5 | deb-systemd-invoke stop evcc.service > /dev/null || true
6 | fi
7 |
--------------------------------------------------------------------------------
/plugin/auth/config.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strings"
7 |
8 | reg "github.com/evcc-io/evcc/util/registry"
9 | "golang.org/x/oauth2"
10 | )
11 |
12 | var registry = reg.New[oauth2.TokenSource]("auth")
13 |
14 | // NewFromConfig creates auth from configuration
15 | func NewFromConfig(ctx context.Context, typ string, other map[string]any) (oauth2.TokenSource, error) {
16 | factory, err := registry.Get(strings.ToLower(typ))
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | v, err := factory(ctx, other)
22 | if err != nil {
23 | err = fmt.Errorf("cannot create auth type '%s': %w", typ, err)
24 | }
25 |
26 | return v, err
27 | }
28 |
--------------------------------------------------------------------------------
/plugin/golang/stdlib/generate.go:
--------------------------------------------------------------------------------
1 | package stdlib
2 |
3 | import "reflect"
4 |
5 | // go:generate yaegi extract fmt
6 | // go:generate yaegi extract math
7 | // go:generate yaegi extract strings
8 | // go:generate yaegi extract time
9 |
10 | // Symbols variable stores the map of stdlib symbols per package.
11 | var Symbols = map[string]map[string]reflect.Value{}
12 |
--------------------------------------------------------------------------------
/plugin/http_limit.go:
--------------------------------------------------------------------------------
1 | package plugin
2 |
3 | import "sync"
4 |
5 | var (
6 | httpMu sync.Mutex
7 | httpMutexes = map[string]*sync.Mutex{}
8 | )
9 |
10 | func muForKey(key string) *sync.Mutex {
11 | httpMu.Lock()
12 | defer httpMu.Unlock()
13 |
14 | if mu, ok := httpMutexes[key]; ok {
15 | return mu
16 | }
17 |
18 | mu := new(sync.Mutex)
19 | httpMutexes[key] = mu
20 | return mu
21 | }
22 |
--------------------------------------------------------------------------------
/plugin/method.go:
--------------------------------------------------------------------------------
1 | package plugin
2 |
3 | //go:generate go tool enumer -type Method -text
4 | type Method int
5 |
6 | const (
7 | _ Method = iota
8 | Energy
9 | Power
10 | Soc
11 | )
12 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Config} */
2 | export default {
3 | printWidth: 100,
4 | trailingComma: "es5",
5 | plugins: ["prettier-plugin-sh"],
6 | };
7 |
--------------------------------------------------------------------------------
/server/assets/assets.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import "io/fs"
4 |
5 | var (
6 | // Web is the embedded dist file system
7 | Web fs.FS
8 |
9 | // I18n is the embedded i18n file system
10 | I18n fs.FS
11 | )
12 |
13 | // Live indicates assets are passed-through from filesystem
14 | func Live() bool {
15 | return Web != nil
16 | }
17 |
--------------------------------------------------------------------------------
/server/assets/assets_live.go:
--------------------------------------------------------------------------------
1 | //go:build !release
2 |
3 | package assets
4 |
5 | import (
6 | "os"
7 | )
8 |
9 | func init() {
10 | Web = os.DirFS("dist")
11 | I18n = os.DirFS("i18n")
12 | }
13 |
--------------------------------------------------------------------------------
/server/db/settings/api.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | //go:generate go tool mockgen -package settings -destination mock.go -mock_names API=MockAPI github.com/evcc-io/evcc/server/db/settings API
4 |
5 | type API interface {
6 | String(key string) (string, error)
7 | SetString(key string, value string)
8 | }
9 |
--------------------------------------------------------------------------------
/server/eebus/eebus_test.go:
--------------------------------------------------------------------------------
1 | package eebus
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | "go.yaml.in/yaml/v4"
8 | )
9 |
10 | func TestConfig(t *testing.T) {
11 | conf := `
12 | certificate:
13 | private: |
14 | -----BEGIN EC PRIVATE KEY-----
15 | MHcCfoo==
16 | -----END EC PRIVATE KEY-----
17 | public: |
18 | -----BEGIN CERTIFICATE-----
19 | MIIBbar=
20 | -----END CERTIFICATE-----
21 | `
22 |
23 | var res Config
24 | require.NoError(t, yaml.Unmarshal([]byte(conf), &res))
25 | }
26 |
--------------------------------------------------------------------------------
/server/eebus/helper.go:
--------------------------------------------------------------------------------
1 | package eebus
2 |
3 | import (
4 | "errors"
5 |
6 | eebusapi "github.com/enbility/eebus-go/api"
7 | "github.com/evcc-io/evcc/api"
8 | )
9 |
10 | func WrapError(err error) error {
11 | if errors.Is(err, eebusapi.ErrDataNotAvailable) {
12 | return api.ErrNotAvailable
13 | }
14 | return err
15 | }
16 |
--------------------------------------------------------------------------------
/server/eebus/types.go:
--------------------------------------------------------------------------------
1 | package eebus
2 |
3 | import "github.com/evcc-io/evcc/util"
4 |
5 | const (
6 | BrandName string = "EVCC"
7 | Model string = "HEMS"
8 | )
9 |
10 | // used as common name in cert generation
11 | var DeviceCode = util.Getenv("EEBUS_DEVICE_CODE", "EVCC_HEMS_01")
12 |
13 | type Config struct {
14 | URI string
15 | ShipID string
16 | Interfaces []string
17 | Certificate struct {
18 | Public, Private string
19 | }
20 | }
21 |
22 | // Configured returns true if the EEbus server is configured
23 | func (c Config) Configured() bool {
24 | return len(c.Certificate.Public) > 0 && len(c.Certificate.Private) > 0
25 | }
26 |
--------------------------------------------------------------------------------
/server/log.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import "github.com/evcc-io/evcc/util"
4 |
5 | var log = util.NewLogger("server")
6 |
--------------------------------------------------------------------------------
/server/mcp/tools.go:
--------------------------------------------------------------------------------
1 | package mcp
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/modelcontextprotocol/go-sdk/mcp"
7 | )
8 |
9 | func docsTool(_ context.Context, _ *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, any, error) {
10 | return &mcp.CallToolResult{
11 | Content: []mcp.Content{
12 | &mcp.ResourceLink{
13 | URI: "https://docs.evcc.io",
14 | Name: "evcc-docs",
15 | Title: "evcc documentation",
16 | MIMEType: "text/html",
17 | },
18 | },
19 | }, nil, nil
20 | }
21 |
--------------------------------------------------------------------------------
/server/modbus/readonlymode.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | // go:generate go tool enumer -type ReadOnlyMode -trimprefix ReadOnly -transform=lower
4 |
5 | type ReadOnlyMode int
6 |
7 | const (
8 | ReadOnlyFalse ReadOnlyMode = iota
9 | ReadOnlyDeny
10 | ReadOnlyTrue
11 | )
12 |
--------------------------------------------------------------------------------
/server/openapi.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | //go:generate go tool openapi openapi.yaml mcp/openapi.json
4 | //go:generate go tool openapi-mcp --doc mcp/openapi.md openapi.yaml
5 |
--------------------------------------------------------------------------------
/server/openapi_test.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/getkin/kin-openapi/openapi3"
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestOpenAPIValidation(t *testing.T) {
11 | loader := openapi3.NewLoader()
12 | doc, err := loader.LoadFromFile("openapi.yaml")
13 | require.NoError(t, err)
14 | require.NoError(t, doc.Validate(loader.Context))
15 | }
16 |
--------------------------------------------------------------------------------
/server/product.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | type product struct {
4 | Name string `json:"name"`
5 | Template string `json:"template"`
6 | Group string `json:"group,omitempty"`
7 | }
8 |
9 | type products []product
10 |
--------------------------------------------------------------------------------
/server/uds_windows.go:
--------------------------------------------------------------------------------
1 | //go:build windows
2 |
3 | package server
4 |
5 | import "github.com/evcc-io/evcc/core/site"
6 |
7 | // HealthListener attaches listener to unix domain socket
8 | func HealthListener(_ site.API) {
9 | // nop
10 | }
11 |
--------------------------------------------------------------------------------
/server/updater/run.go:
--------------------------------------------------------------------------------
1 | //go:build !gokrazy
2 |
3 | package updater
4 |
5 | import (
6 | "github.com/evcc-io/evcc/util"
7 | "github.com/google/go-github/v32/github"
8 | )
9 |
10 | // Run regularly checks version
11 | func Run(log *util.Logger, httpd webServer, outChan chan<- util.Param) {
12 | u := &watch{
13 | log: log,
14 | outChan: outChan,
15 | repo: NewRepo(log, owner, repository),
16 | }
17 |
18 | c := make(chan *github.RepositoryRelease, 1)
19 | go u.watchReleases(util.Version, c) // endless
20 |
21 | for rel := range c {
22 | u.Send("availableVersion", *rel.TagName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tariff/elering/types.go:
--------------------------------------------------------------------------------
1 | package elering
2 |
3 | const URI = "https://dashboard.elering.ee/api"
4 |
5 | type NpsPrice struct {
6 | Success bool
7 | Data map[string][]Price
8 | }
9 |
10 | type Price struct {
11 | Timestamp int64
12 | Price float64
13 | }
14 |
--------------------------------------------------------------------------------
/tariff/octopus/graphql/errors.go:
--------------------------------------------------------------------------------
1 | package graphql
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | var (
8 | ErrAccountNotFound = errors.New("unable to find configured account")
9 | ErrMultipleAccounts = errors.New("multiple accounts on this api key - specific an account to use in configuration")
10 | ErrNoAccounts = errors.New("no accounts on this api key")
11 | )
12 |
--------------------------------------------------------------------------------
/tariff/proxy_cache_error.go:
--------------------------------------------------------------------------------
1 | package tariff
2 |
3 | import "github.com/evcc-io/evcc/api"
4 |
5 | type proxyError struct {
6 | error
7 | }
8 |
9 | var _ api.Tariff = (*proxyError)(nil)
10 |
11 | func (t *proxyError) Rates() (api.Rates, error) {
12 | return api.Rates{}, t.error
13 | }
14 |
15 | func (t *proxyError) Type() api.TariffType {
16 | return 0 // unknown
17 | }
18 |
--------------------------------------------------------------------------------
/tariff/smartenergy/types.go:
--------------------------------------------------------------------------------
1 | package smartenergy
2 |
3 | import "time"
4 |
5 | const URI = "https://apis.smartenergy.at/market/v1/price"
6 |
7 | type Prices struct {
8 | Data []Price
9 | }
10 |
11 | type Price struct {
12 | Date time.Time
13 | Value float64
14 | }
15 |
--------------------------------------------------------------------------------
/tariff/template.go:
--------------------------------------------------------------------------------
1 | package tariff
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/util/templates"
8 | )
9 |
10 | func init() {
11 | registry.AddCtx("template", NewTariffFromTemplateConfig)
12 | }
13 |
14 | func NewTariffFromTemplateConfig(ctx context.Context, other map[string]any) (api.Tariff, error) {
15 | instance, err := templates.RenderInstance(templates.Tariff, other)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | return NewProxyFromConfig(ctx, instance.Type, instance.Other)
21 | }
22 |
--------------------------------------------------------------------------------
/tariff/types.go:
--------------------------------------------------------------------------------
1 | package tariff
2 |
3 | type Typed struct {
4 | Type string `json:"type"`
5 | Tariff string `json:"tariff"`
6 | Other map[string]any `mapstructure:",remain" yaml:",inline"`
7 | }
8 |
9 | func (t Typed) Name() string {
10 | if t.Type == "template" {
11 | return t.Tariff
12 | }
13 | return t.Type
14 | }
15 |
16 | type FromTo struct {
17 | From, To int
18 | }
19 |
20 | func (ft FromTo) IsActive(hour int) bool {
21 | return ft.From == 0 && ft.To == 0 ||
22 | ft.From < ft.To && ft.From <= hour && hour <= ft.To ||
23 | ft.From > ft.To && (ft.From <= hour || hour <= ft.To)
24 | }
25 |
--------------------------------------------------------------------------------
/templates/definition/charger/abb.yaml:
--------------------------------------------------------------------------------
1 | template: abb
2 | products:
3 | - brand: ABB
4 | description:
5 | generic: Terra AC
6 | capabilities: ["mA"]
7 | requirements:
8 | description:
9 | de: Erfordert Firmware >= 1.6.5
10 | en: Requires firmware >= 1.6.5
11 | evcc: ["sponsorship"]
12 | params:
13 | - name: modbus
14 | choice: ["rs485", "tcpip"]
15 | render: |
16 | type: abb
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/abl-em4.yaml:
--------------------------------------------------------------------------------
1 | template: abl-em4
2 | products:
3 | - brand: ABL
4 | description:
5 | generic: eM4 Single (SBCx)
6 | - brand: ABL
7 | description:
8 | generic: eM4 Twin (SBCx)
9 | capabilities: ["mA"]
10 | requirements:
11 | evcc: ["sponsorship"]
12 | params:
13 | - name: modbus
14 | choice: ["tcpip"]
15 | id: 255
16 | - name: connector
17 | default: 1
18 | render: |
19 | type: abl-em4
20 | {{- include "modbus" . }}
21 | connector: {{ .connector }}
22 |
--------------------------------------------------------------------------------
/templates/definition/charger/abl.yaml:
--------------------------------------------------------------------------------
1 | template: abl
2 | products:
3 | - brand: ABL
4 | description:
5 | generic: eMH1
6 | - brand: ABL
7 | description:
8 | generic: eMH2
9 | - brand: SENEC
10 | description:
11 | generic: Wallbox pro
12 | capabilities: ["mA"]
13 | requirements:
14 | evcc: ["sponsorship"]
15 | params:
16 | - name: modbus
17 | choice: ["rs485"]
18 | baudrate: 38400
19 | comset: 8E1
20 | - name: timeout
21 | render: |
22 | type: abl
23 | {{- include "modbus" . }}
24 | timeout: {{ .timeout }}
25 |
--------------------------------------------------------------------------------
/templates/definition/charger/alpitronic.yaml:
--------------------------------------------------------------------------------
1 | template: alpitronic
2 | products:
3 | - brand: Alpitronic
4 | description:
5 | generic: Hypercharger
6 | capabilities: ["mA", "rfid", "iso151182"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | - name: connector
13 | default: 1
14 | render: |
15 | type: alpitronic
16 | {{- include "modbus" . }}
17 | connector: {{ .connector }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/amperfied-solar.yaml:
--------------------------------------------------------------------------------
1 | template: amperfied-solar
2 | products:
3 | - brand: Amperfied
4 | description:
5 | generic: Wallbox connect.solar
6 | capabilities: ["mA", "rfid", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | id: 255
13 | render: |
14 | type: amperfied
15 | {{- include "modbus" . }}
16 | phases1p3p: true
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/amperfied.yaml:
--------------------------------------------------------------------------------
1 | template: amperfied
2 | products:
3 | - brand: Amperfied
4 | description:
5 | generic: Wallbox connect.home
6 | - brand: Amperfied
7 | description:
8 | generic: Wallbox connect.business
9 | capabilities: ["mA", "rfid"]
10 | requirements:
11 | evcc: ["sponsorship"]
12 | params:
13 | - name: modbus
14 | choice: ["tcpip"]
15 | id: 255
16 | render: |
17 | type: amperfied
18 | {{- include "modbus" . }}
19 |
--------------------------------------------------------------------------------
/templates/definition/charger/compleo-duo.yaml:
--------------------------------------------------------------------------------
1 | template: compleo-duo
2 | products:
3 | - brand: Compleo
4 | description:
5 | generic: Duo
6 | capabilities: ["mA", "rfid", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | - name: connector
13 | render: |
14 | type: compleo
15 | {{- include "modbus" . }}
16 | connector: {{ .connector }}
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/compleo-solo.yaml:
--------------------------------------------------------------------------------
1 | template: compleo-solo
2 | products:
3 | - brand: Compleo
4 | description:
5 | generic: Solo
6 | capabilities: ["mA", "rfid"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | render: |
13 | type: compleo
14 | {{- include "modbus" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/dadapower.yaml:
--------------------------------------------------------------------------------
1 | template: dadapower
2 | products:
3 | - brand: Dadapower
4 | description:
5 | generic: Premium Wallbox
6 | capabilities: ["1p3p", "mA", "rfid"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | id: 1
13 | render: |
14 | type: dadapower
15 | {{- include "modbus" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/daheimladen-pro.yaml:
--------------------------------------------------------------------------------
1 | template: daheimladen-pro
2 | products:
3 | - brand: DaheimLaden
4 | description:
5 | generic: Smart/Touch Pro
6 | requirements:
7 | description:
8 | de: Die Phasenumschaltung benötigt mindestens die Firmware-Version "M3W_3.11". Während der Umschaltung pausiert die Ladung für zwei Minuten.
9 | en: Phase switching requires at least firmware version "M3W_3.11." Charging pauses for two minutes during the phase switching.
10 | capabilities: ["1p3p", "mA"]
11 | params:
12 | - name: host
13 | - name: port
14 | default: 502
15 | render: |
16 | type: daheimladen
17 | uri: {{ .host }}:{{ .port }}
18 | phases1p3p: true
19 |
--------------------------------------------------------------------------------
/templates/definition/charger/delta.yaml:
--------------------------------------------------------------------------------
1 | template: delta
2 | products:
3 | - brand: Delta
4 | description:
5 | generic: AC Max Basic
6 | - brand: Delta
7 | description:
8 | generic: AC MAX Smart
9 | - brand: Delta
10 | description:
11 | generic: SLIM Charger
12 | - brand: Delta
13 | description:
14 | generic: Ultra Fast Charger
15 | capabilities: ["mA", "rfid"]
16 | requirements:
17 | evcc: ["sponsorship"]
18 | params:
19 | - name: modbus
20 | choice: ["rs485", "tcpip"]
21 | baudrate: 115200
22 | - name: connector
23 | render: |
24 | type: delta
25 | {{- include "modbus" . }}
26 | connector: {{ .connector }}
27 |
--------------------------------------------------------------------------------
/templates/definition/charger/eebus.yaml:
--------------------------------------------------------------------------------
1 | template: eebus
2 | products:
3 | - description:
4 | de: EEBUS kompatibel
5 | en: EEBUS compatible
6 | group: generic
7 | capabilities: ["mA"]
8 | requirements:
9 | evcc: ["eebus"]
10 | params:
11 | - preset: eebus
12 | render: |
13 | {{ include "eebus" . }}
14 | meter: true
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/em2go-duo.yaml:
--------------------------------------------------------------------------------
1 | template: em2go-duo
2 | products:
3 | - brand: EM2GO
4 | description:
5 | generic: Duo Power
6 | params:
7 | - name: modbus
8 | choice: ["tcpip"]
9 | id: 255
10 | - name: connector
11 | default: 1
12 | render: |
13 | type: em2go-duo
14 | {{- include "modbus" . }}
15 | connector: {{ .connector }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/em2go-home.yaml:
--------------------------------------------------------------------------------
1 | template: em2go-home
2 | products:
3 | - brand: EM2GO
4 | description:
5 | generic: Home
6 | capabilities: ["1p3p", "mA"]
7 | requirements:
8 | description:
9 | de: "Benötigt FW version >= E3C_V1.1. mA Regelung benötigt FW version >= E3C_V1.3."
10 | en: "Requires FW Version >= E3C_V1.1. mA regulation requires FW version >= E3C_V1.3."
11 | params:
12 | - name: host
13 | render: |
14 | type: em2go-home
15 | uri: {{ .host }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/em2go.yaml:
--------------------------------------------------------------------------------
1 | template: em2go
2 | products:
3 | - brand: EM2GO
4 | description:
5 | generic: Pro Power (OCPP/ONC)
6 | capabilities: ["mA"]
7 | requirements:
8 | description:
9 | de: "Aktuelle Firmware mit Modbus-Unterstützung notwendig (Pro Power: 1.01 bzw. OCPP/ONC: 3.15)"
10 | en: "Recent firmware with Modbus support required (Pro Power: 1.01 and OCPP/ONC: 3.15)"
11 | params:
12 | - name: modbus
13 | choice: ["tcpip"]
14 | id: 255
15 | render: |
16 | type: em2go
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/eprowallbox.yaml:
--------------------------------------------------------------------------------
1 | template: eprowallbox
2 | products:
3 | - brand: Free2Move
4 | description:
5 | generic: eProWallbox
6 | - brand: Free2Move
7 | description:
8 | generic: eProWallbox Move
9 | capabilities: ["mA"]
10 | requirements:
11 | evcc: ["sponsorship"]
12 | params:
13 | - name: modbus
14 | choice: ["rs485"]
15 | render: |
16 | type: eprowallbox
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/etrel-duo.yaml:
--------------------------------------------------------------------------------
1 | template: etrel-duo
2 | products:
3 | - brand: Etrel
4 | description:
5 | generic: INCH Duo
6 | capabilities: ["mA"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | description:
10 | de: Die Wallbox muss sich im "Power" Modus befinden.
11 | en: The charger must be switched to "Power" charging mode.
12 | params:
13 | - name: connector
14 | - name: host
15 | - name: port
16 | default: 502
17 | render: |
18 | type: etrel
19 | connector: {{ .connector }}
20 | uri: {{ .host }}:{{ .port }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/etrel.yaml:
--------------------------------------------------------------------------------
1 | template: etrel
2 | products:
3 | - brand: Etrel
4 | description:
5 | generic: INCH
6 | - brand: Sonnen
7 | description:
8 | generic: sonnenCharger
9 | capabilities: ["mA"]
10 | requirements:
11 | evcc: ["sponsorship"]
12 | description:
13 | de: Die Wallbox muss sich im "Power" Modus befinden.
14 | en: The charger must be switched to "Power" charging mode.
15 | params:
16 | - name: host
17 | - name: port
18 | default: 502
19 | render: |
20 | type: etrel
21 | uri: {{ .host }}:{{ .port }}
22 |
--------------------------------------------------------------------------------
/templates/definition/charger/evse-din.yaml:
--------------------------------------------------------------------------------
1 | template: evse-din
2 | covers:
3 | - evse_din
4 | products:
5 | - brand: Stark in Strom
6 | description:
7 | generic: Easy
8 | - description:
9 | generic: EVSE DIN
10 | params:
11 | - name: modbus
12 | choice: ["rs485"]
13 | baudrate: 9600
14 | comset: 8N1
15 | render: |
16 | type: evsedin
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/evsewifi.yaml:
--------------------------------------------------------------------------------
1 | template: evsewifi
2 | products:
3 | - description:
4 | generic: EVSE-WiFi
5 | params:
6 | - name: host
7 | render: |
8 | type: evsewifi
9 | uri: http://{{ .host }}
10 |
--------------------------------------------------------------------------------
/templates/definition/charger/fronius-wattpilot.yaml:
--------------------------------------------------------------------------------
1 | template: fronius-wattpilot
2 | deprecated: true
3 | products:
4 | - brand: Fronius
5 | description:
6 | generic: Wattpilot
7 | capabilities: ["1p3p", "rfid"]
8 | requirements:
9 | description:
10 | de: |
11 | Benötigt mindestens Firmware 36.3 oder neuer.
12 | en: |
13 | Requires firmware 36.3 or later.
14 | params:
15 | - name: host
16 | - name: password
17 | mask: true
18 | render: |
19 | type: wattpilot
20 | uri: {{ .host }}
21 | password: {{ .password }}
22 |
--------------------------------------------------------------------------------
/templates/definition/charger/go-e.yaml:
--------------------------------------------------------------------------------
1 | template: go-e
2 | products:
3 | - brand: go-e
4 | description:
5 | generic: Charger HOMEfix
6 | - brand: go-e
7 | description:
8 | generic: Charger PRO
9 | capabilities: ["rfid"]
10 | requirements:
11 | description:
12 | en: Requires firmware 040.0 or later. HTTP API v1 or v2 must be activated.
13 | de: Benötigt mindestens Firmware 040.0 oder neuer. Das HTTP API v1 oder v2 muss aktiviert sein.
14 | evcc: ["sponsorship"]
15 | params:
16 | - name: host
17 | render: |
18 | type: go-e
19 | uri: http://{{ .host }}
20 |
--------------------------------------------------------------------------------
/templates/definition/charger/hardybarth-ecb1.yaml:
--------------------------------------------------------------------------------
1 | template: hardybarth-ecb1
2 | products:
3 | - brand: Hardy Barth
4 | description:
5 | generic: cPH1
6 | - brand: echarge
7 | description:
8 | generic: cPH1
9 | requirements:
10 | evcc: ["sponsorship"]
11 | description:
12 | de: Als Betriebsmodus muss `manual` ausgewählt sein
13 | en: Charge mode must be configured as `manual`
14 | params:
15 | - name: host
16 | - name: connector
17 | default: 1
18 | advanced: true
19 | render: |
20 | type: hardybarth-ecb1
21 | uri: http://{{ .host }}
22 | chargecontrol: {{ .connector }}
23 | meter: {{ .connector }}
24 |
--------------------------------------------------------------------------------
/templates/definition/charger/hesotec.yaml:
--------------------------------------------------------------------------------
1 | template: hesotec
2 | products:
3 | - brand: Hesotec
4 | description:
5 | generic: eSat
6 | - brand: Hesotec
7 | description:
8 | generic: eBox
9 | requirements:
10 | evcc: ["sponsorship"]
11 | params:
12 | - name: modbus
13 | choice: ["tcpip"]
14 | render: |
15 | type: hesotec
16 | {{- include "modbus" . }}
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/homewizard.yaml:
--------------------------------------------------------------------------------
1 | template: homewizard
2 | products:
3 | - brand: HomeWizard
4 | group: switchsockets
5 | params:
6 | - name: host
7 | - preset: switchsocket
8 | render: |
9 | type: homewizard
10 | uri: http://{{ .host }}
11 |
--------------------------------------------------------------------------------
/templates/definition/charger/innogy-ebox.yaml:
--------------------------------------------------------------------------------
1 | template: innogy-ebox
2 | products:
3 | - brand: Innogy
4 | description:
5 | generic: eBox
6 | - brand: E.ON Drive
7 | description:
8 | generic: eBox
9 | - brand: Compleo
10 | description:
11 | generic: eBox
12 | capabilities: ["mA"]
13 | requirements:
14 | evcc: ["sponsorship"]
15 | params:
16 | - name: modbus
17 | choice: ["tcpip"]
18 | render: |
19 | type: innogy
20 | {{- include "modbus" . }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/kse.yaml:
--------------------------------------------------------------------------------
1 | template: kse
2 | products:
3 | - brand: KSE
4 | description:
5 | generic: wBX16
6 | capabilities: ["rfid", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["rs485"]
12 | baudrate: 9600
13 | comset: 8E1
14 | id: 100
15 | render: |
16 | type: kse
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/mennekes-hcc3.yaml:
--------------------------------------------------------------------------------
1 | template: mennekes-hcc3
2 | covers: ["amtron", "menneckes-hcc3"]
3 | products:
4 | - brand: Mennekes
5 | description:
6 | generic: AMTRON Xtra
7 | - brand: Mennekes
8 | description:
9 | generic: AMTRON Premium
10 | requirements:
11 | evcc: ["sponsorship"]
12 | params:
13 | - name: modbus
14 | choice: ["tcpip"]
15 | id: 255
16 | render: |
17 | type: mennekes-hcc3
18 | {{- include "modbus" . }}
19 |
--------------------------------------------------------------------------------
/templates/definition/charger/mystrom.yaml:
--------------------------------------------------------------------------------
1 | template: mystrom
2 | products:
3 | - brand: myStrom
4 | description:
5 | generic: Switch
6 | group: switchsockets
7 | params:
8 | - name: host
9 | - preset: switchsocket
10 | render: |
11 | type: mystrom
12 | uri: http://{{ .host }}
13 | {{ include "switchsocket" . }}
14 |
--------------------------------------------------------------------------------
/templates/definition/charger/neoom-n-plus.yaml:
--------------------------------------------------------------------------------
1 | template: neoom-n-plus
2 | products:
3 | - brand: Neoom
4 | description:
5 | generic: N+
6 | capabilities: ["mA", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | render: |
13 | type: compleo
14 | {{- include "modbus" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/neoom-n.yaml:
--------------------------------------------------------------------------------
1 | template: neoom-n
2 | products:
3 | - brand: Neoom
4 | description:
5 | generic: N
6 | capabilities: ["mA", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | render: |
13 | type: compleo
14 | {{- include "modbus" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/nrgkick-bluetooth.yaml:
--------------------------------------------------------------------------------
1 | template: nrgkick-bluetooth
2 | deprecated: true
3 | products:
4 | - brand: NRGkick
5 | description:
6 | generic: Bluetooth
7 | requirements:
8 | description:
9 | de: NRGkick Ladeeinheit via Bluetooth (älter als 2022/2023)
10 | en: NRGkick charging unit via Bluetooth (older than 2022/2023)
11 | params:
12 | - name: mac
13 | required: true
14 | - name: pin
15 | required: true
16 | mask: true
17 | render: |
18 | type: nrgkick-bluetooth
19 | mac: {{ .mac }}
20 | pin: {{ .pin }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/nrgkick-connect.yaml:
--------------------------------------------------------------------------------
1 | template: nrgkick-connect
2 | products:
3 | - brand: NRGkick
4 | description:
5 | generic: Connect
6 | requirements:
7 | description:
8 | de: NRGkick Ladeeinheit via HTTP (älter als 2022/2023)
9 | en: NRGkick charging unit via HTTP (older than 2022/2023)
10 | params:
11 | - name: host
12 | - name: mac
13 | required: true
14 | - name: password
15 | required: true
16 | render: |
17 | type: nrgkick-connect
18 | uri: http://{{ .host }}
19 | mac: {{ .mac }} # BT device MAC address (sudo hcitool lescan)
20 | password: {{ .password }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/obo.yaml:
--------------------------------------------------------------------------------
1 | template: obo
2 | products:
3 | - brand: EcoHarmony
4 | description:
5 | generic: EVSE EPC 2.0 Plus
6 | - brand: OBO Bettermann
7 | description:
8 | generic: Ion
9 | - brand: Viridian EV
10 | description:
11 | generic: EVSE EPC 2.0 Plus
12 | params:
13 | - name: modbus
14 | choice: ["rs485", "tcpip"]
15 | baudrate: 19200
16 | comset: 8E1
17 | id: 101
18 | render: |
19 | type: obo
20 | {{- include "modbus" . }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-abb-tac.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-abb-tac
2 | covers: ["ocpp-abb"]
3 | products:
4 | - brand: ABB
5 | description:
6 | generic: Terra AC (OCPP)
7 | capabilities: ["mA", "rfid"]
8 | requirements:
9 | evcc: ["sponsorship", "skiptest"]
10 | description:
11 | generic: https://library.e.abb.com/public/8f07987a3a284da6bf4e4f8f53cd6502/ABB_Terra_AC_Charger_OCPP1.6_ImplementationOverview%20_v1.8_FW1.6.6.pdf
12 | params:
13 | - preset: ocpp
14 | render: |
15 | {{ include "ocpp" . }}
16 | stacklevelzero: true
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-abl.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-abl
2 | products:
3 | - brand: ABL
4 | description:
5 | generic: eMH2 (OCPP)
6 | - brand: ABL
7 | description:
8 | generic: eMH3 (OCPP)
9 | - brand: ABL
10 | description:
11 | generic: eM4 Single (OCPP)
12 | - brand: ABL
13 | description:
14 | generic: eM4 Twin (OCPP)
15 | capabilities: ["mA", "rfid"]
16 | requirements:
17 | evcc: ["sponsorship", "skiptest"]
18 | params:
19 | - preset: ocpp
20 | render: |
21 | {{ include "ocpp" . }}
22 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-alfen.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-alfen
2 | products:
3 | - brand: Alfen
4 | description:
5 | generic: Eve (OCPP)
6 | capabilities: ["mA", "rfid", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-autoaid.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-autoaid
2 | products:
3 | - brand: Autoaid
4 | description:
5 | generic: Intelligent Wallbox
6 | - brand: Autoaid
7 | description:
8 | generic: Business Wallbox
9 | capabilities: ["rfid"]
10 | requirements:
11 | evcc: ["sponsorship", "skiptest"]
12 | params:
13 | - preset: ocpp
14 | render: |
15 | {{ include "ocpp" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-beny.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-beny
2 | products:
3 | - brand: ZJ Beny
4 | description:
5 | generic: BCP EV charger
6 | capabilities: ["rfid"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-chargeamps.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-chargeamps
2 | products:
3 | - brand: Charge Amps
4 | description:
5 | generic: Halo
6 | capabilities: ["rfid"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-elecq.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-elecq
2 | products:
3 | - brand: Elecq
4 | description:
5 | generic: Home
6 | - brand: Elecq
7 | description:
8 | generic: Biz
9 | - brand: Elecq
10 | description:
11 | generic: Station
12 | requirements:
13 | evcc: ["sponsorship", "skiptest"]
14 | params:
15 | - preset: ocpp
16 | render: |
17 | {{ include "ocpp" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-enercab.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-enercab
2 | products:
3 | - brand: enercab
4 | description:
5 | generic: smart
6 | - brand: eledio
7 | description:
8 | generic: go
9 | capabilities: ["1p3p"]
10 | requirements:
11 | description:
12 | generic: |
13 | https://www.enercab.at/index.php?controller=attachment&id_attachment=311
14 | evcc: ["sponsorship", "skiptest"]
15 | params:
16 | - preset: ocpp
17 | render: |
18 | {{ include "ocpp" . }}
19 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-enplus.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-enplus
2 | products:
3 | - brand: EN+
4 | description:
5 | generic: AC EV Charger
6 | capabilities: ["rfid"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-entratek.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-entratek
2 | products:
3 | - brand: EntraTek
4 | description:
5 | generic: Power Dot Fix
6 | - brand: EntraTek
7 | description:
8 | generic: Power Dot Pro 2
9 | capabilities: ["rfid"]
10 | requirements:
11 | evcc: ["sponsorship", "skiptest"]
12 | params:
13 | - preset: ocpp
14 | render: |
15 | {{ include "ocpp" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-esolutions.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-esolutions
2 | products:
3 | - brand: Free2move eSolutions
4 | description:
5 | generic: eProWallbox
6 | capabilities: ["rfid"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-evbox-elvi.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-evbox-elvi
2 | covers: ["elvi"]
3 | products:
4 | - brand: EVBox
5 | description:
6 | generic: Elvi
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | - name: meter
12 | type: bool
13 | default: true
14 | - name: meterinterval
15 | deprecated: true
16 | render: |
17 | {{ include "ocpp" . }}
18 | {{- if eq .meter "false" }}
19 | metervalues: Current.Offered
20 | {{- end }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-goe.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-goe
2 | covers: ["ocpp-fronius-wattpilot"]
3 | products:
4 | - brand: go-e
5 | description:
6 | generic: Charger V3 (OCPP)
7 | - brand: go-e
8 | description:
9 | generic: Charger Gemini (OCPP)
10 | - brand: go-e
11 | description:
12 | generic: Charger PRO (OCPP)
13 | - brand: Fronius
14 | description:
15 | generic: Wattpilot (OCPP)
16 | capabilities: ["rfid", "1p3p"]
17 | requirements:
18 | evcc: ["sponsorship", "skiptest"]
19 | params:
20 | - preset: ocpp
21 | render: |
22 | {{ include "ocpp" . }}
23 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-huawei.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-huawei
2 | products:
3 | - brand: Huawei
4 | description:
5 | generic: SCharger-7KS-S0
6 | - brand: Huawei
7 | description:
8 | generic: SCharger-22KT-S0
9 | requirements:
10 | evcc: ["sponsorship", "skiptest"]
11 | params:
12 | - preset: ocpp
13 | render: |
14 | {{ include "ocpp" . }}
15 | forcepowerctrl: true
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-orbis.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-orbis
2 | covers: ["orbis-viaris"]
3 | products:
4 | - brand: Orbis
5 | description:
6 | generic: Viaris
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-sungrow.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-sungrow
2 | products:
3 | - brand: Sungrow
4 | description:
5 | generic: AC011E
6 | capabilities: ["mA", "rfid"]
7 | requirements:
8 | evcc: ["sponsorship", "skiptest"]
9 | params:
10 | - preset: ocpp
11 | render: |
12 | {{ include "ocpp" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/ocpp-zaptec.yaml:
--------------------------------------------------------------------------------
1 | template: ocpp-zaptec
2 | products:
3 | - brand: Zaptec
4 | description:
5 | generic: Go (OCPP)
6 | capabilities: ["rfid"]
7 | requirements:
8 | description:
9 | generic: |
10 | OCPP Native mode
11 |
12 | https://help.zaptec.com/hc/en-001/articles/22330328601489-Zaptec-Go-OCPP-Native-configuration-guide#h_01HP261F5NP6Z9VY0MVHJCZEBJ
13 | evcc: ["sponsorship", "skiptest"]
14 | params:
15 | - preset: ocpp
16 | render: |
17 | {{ include "ocpp" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/openevse.yaml:
--------------------------------------------------------------------------------
1 | template: openevse
2 | products:
3 | - brand: OpenEVSE
4 | requirements:
5 | description:
6 | en: Requires firmware 7.0 or later.
7 | de: Benötigt mindestens Firmware 7.0 oder neuer.
8 | params:
9 | - name: host
10 | - name: user
11 | - name: password
12 | mask: true
13 | render: |
14 | type: openevse
15 | uri: http://{{ .host }}
16 | user: {{ .user }}
17 | password: {{ .password }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/openwb-pro.yaml:
--------------------------------------------------------------------------------
1 | template: openwb-pro
2 | products:
3 | - brand: openWB
4 | description:
5 | generic: Pro
6 | capabilities: ["1p3p", "mA", "iso151182"]
7 | params:
8 | - name: host
9 | render: |
10 | type: openwbpro
11 | uri: http://{{ .host }}
12 |
--------------------------------------------------------------------------------
/templates/definition/charger/pantabox.yaml:
--------------------------------------------------------------------------------
1 | template: pantabox
2 | products:
3 | - brand: INRO
4 | description:
5 | generic: Pantabox
6 | params:
7 | - name: host
8 | render: |
9 | type: pantabox
10 | uri: http://{{ .host }}
11 |
--------------------------------------------------------------------------------
/templates/definition/charger/pcelectric-garo.yaml:
--------------------------------------------------------------------------------
1 | template: pcelectric-garo
2 | products:
3 | - brand: PC Electric
4 | description:
5 | generic: Garo
6 | requirements:
7 | evcc: ["sponsorship"]
8 | description:
9 | de: Es können momentan nur als Master konfigurierte Geräte verwendet werden!
10 | en: Only devices configured as master can be used right now!
11 | params:
12 | - name: host
13 | - name: port
14 | default: 8080
15 | render: |
16 | type: garo
17 | uri: http://{{ .host }}:{{ .port }}/servlet
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/phoenix-charx.yaml:
--------------------------------------------------------------------------------
1 | template: phoenix-charx
2 | products:
3 | - brand: Phoenix Contact
4 | description:
5 | generic: CHARX
6 | - brand: LadeFoxx
7 | description:
8 | generic: EvLoad
9 | - brand: LadeFoxx
10 | description:
11 | generic: Mikro 2.0
12 | - brand: Veton
13 | description:
14 | generic: One
15 | - brand: Veton
16 | description:
17 | generic: Two
18 | - brand: Veton
19 | description:
20 | generic: Wall
21 | params:
22 | - name: modbus
23 | choice: ["tcpip"]
24 | id: 255
25 | - name: connector
26 | render: |
27 | type: phoenix-charx
28 | {{- include "modbus" . }}
29 | connector: {{ .connector }}
30 |
--------------------------------------------------------------------------------
/templates/definition/charger/phoenix-em-eth.yaml:
--------------------------------------------------------------------------------
1 | template: phoenix-em-eth
2 | products:
3 | - brand: Phoenix Contact
4 | description:
5 | generic: EM-CP-PP-ETH
6 | params:
7 | - name: modbus
8 | choice: ["tcpip"]
9 | id: 180
10 | render: |
11 | type: phoenix-em-eth
12 | {{- include "modbus" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/phoenix-ev-ser.yaml:
--------------------------------------------------------------------------------
1 | template: phoenix-ev-ser
2 | products:
3 | - brand: Phoenix Contact
4 | description:
5 | generic: EV-SER
6 | params:
7 | - name: modbus
8 | choice: ["rs485"]
9 | render: |
10 | type: phoenix-ev-ser
11 | {{- include "modbus" . }}
12 |
--------------------------------------------------------------------------------
/templates/definition/charger/porsche-pmcc.yaml:
--------------------------------------------------------------------------------
1 | template: pmcc
2 | products:
3 | - brand: Porsche
4 | description:
5 | generic: Mobile Charger Connect
6 | capabilities: ["iso151182", "mA"]
7 | requirements:
8 | evcc: ["eebus"]
9 | params:
10 | - preset: eebus
11 | render: |
12 | {{ include "eebus" . }}
13 | meter: true
14 | vasvw: true
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/porsche-pmcp.yaml:
--------------------------------------------------------------------------------
1 | template: pmcp
2 | products:
3 | - brand: Porsche
4 | description:
5 | generic: Mobile Charger Plus
6 | requirements:
7 | evcc: ["eebus"]
8 | params:
9 | - preset: eebus
10 | render: |
11 | {{ include "eebus" . }}
12 | meter: true
13 |
--------------------------------------------------------------------------------
/templates/definition/charger/pracht-alpha.yaml:
--------------------------------------------------------------------------------
1 | template: pracht-alpha
2 | products:
3 | - brand: Pracht
4 | description:
5 | generic: Alpha XT
6 | - brand: Pracht
7 | description:
8 | generic: XT+
9 | - brand: Pracht
10 | description:
11 | generic: Mono XT
12 | - brand: Pracht
13 | description:
14 | generic: PNI
15 | requirements:
16 | evcc: ["sponsorship"]
17 | params:
18 | - name: modbus
19 | choice: ["rs485", "tcpip"]
20 | baudrate: 9600
21 | comset: 8N1
22 | id: 1
23 | - name: connector
24 | - name: timeout
25 | render: |
26 | type: pracht-alpha
27 | {{- include "modbus" . }}
28 | connector: {{ .connector }}
29 | timeout: {{ .timeout }}
30 |
--------------------------------------------------------------------------------
/templates/definition/charger/pulsares.yaml:
--------------------------------------------------------------------------------
1 | template: pulsares
2 | products:
3 | - brand: Pulsares
4 | description:
5 | generic: SimpleBox
6 | params:
7 | - name: modbus
8 | choice: ["rs485"]
9 | baudrate: 9600
10 | comset: 8N1
11 | render: |
12 | type: pulsares
13 | {{- include "modbus" . }}
14 |
--------------------------------------------------------------------------------
/templates/definition/charger/pulsatrix.yaml:
--------------------------------------------------------------------------------
1 | template: pulsatrix
2 | products:
3 | - brand: Pulsatrix
4 | requirements:
5 | evcc: ["sponsorship"]
6 | params:
7 | - name: host #IP address or hostname (can be found on 3rd page of SECC display)
8 | required: true
9 | help:
10 | en: Shown on 3rd page of SECC display
11 | de: Wird auf der dritten Displayseite des SECC angezeigt
12 | render: |
13 | type: pulsatrix
14 | host: {{ .host }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/scheider-evlink-v3.yaml:
--------------------------------------------------------------------------------
1 | template: schneider-evlink-v3
2 | covers: ["schneider-evlink"]
3 | products:
4 | - brand: Schneider
5 | description:
6 | generic: EVlink Pro
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | id: 255
13 | render: |
14 | type: schneider-v3
15 | {{- include "modbus" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/senec-plus.yaml:
--------------------------------------------------------------------------------
1 | template: senec-plus
2 | products:
3 | - brand: SENEC
4 | description:
5 | generic: Plus
6 | capabilities: ["mA", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | render: |
13 | type: compleo
14 | {{- include "modbus" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/senec-premium.yaml:
--------------------------------------------------------------------------------
1 | template: senec-premium
2 | products:
3 | - brand: SENEC
4 | description:
5 | generic: Premium
6 | capabilities: ["mA", "rfid", "1p3p"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | render: |
13 | type: compleo
14 | {{- include "modbus" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/sigenergy.yaml:
--------------------------------------------------------------------------------
1 | template: sigenergy
2 | products:
3 | - brand: Sigenergy
4 | description:
5 | generic: EVAC series
6 | capabilities: ["mA"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: modbus
11 | choice: ["tcpip"]
12 | id: 1
13 | render: |
14 | type: sigenergy
15 | {{- include "modbus" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/smartevse.yaml:
--------------------------------------------------------------------------------
1 | template: smartevse
2 | products:
3 | - brand: Edgetech
4 | description:
5 | generic: Smart EVSE
6 | capabilities: ["1p3p"]
7 | params:
8 | - name: modbus
9 | choice: ["rs485"]
10 | baudrate: 9600
11 | comset: 8N1
12 | id: 1
13 | render: |
14 | type: smartevse
15 | {{- include "modbus" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/charger/smartwb.yaml:
--------------------------------------------------------------------------------
1 | template: smartwb
2 | products:
3 | - description:
4 | generic: smartWB
5 | params:
6 | - name: host
7 | render: |
8 | type: evsewifi
9 | uri: http://{{ .host }}
10 | meter:
11 | power: true
12 | energy: true
13 | currents: true
14 | voltages: true
15 |
--------------------------------------------------------------------------------
/templates/definition/charger/solax-g2.yaml:
--------------------------------------------------------------------------------
1 | template: solax-g2
2 | products:
3 | - brand: Solax
4 | description:
5 | generic: X3-HAC
6 | capabilities: ["mA"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | description:
10 | de: Die Wallbox muss sich im Modus "Schnell" befinden und vom Wechselrichtersystem entkoppelt sein.
11 | en: The charger must be in “Fast” mode and decoupled from the inverter system.
12 | params:
13 | - name: modbus
14 | choice: ["rs485"]
15 | baudrate: 9600
16 | comset: 8N1
17 | id: 70
18 | render: |
19 | type: solax-g2
20 | {{- include "modbus" . }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/sungrow.yaml:
--------------------------------------------------------------------------------
1 | template: sungrow
2 | products:
3 | - brand: Sungrow
4 | description:
5 | generic: AC011E-01
6 | - brand: Sungrow
7 | description:
8 | generic: AC22E-01
9 | capabilities: ["mA", "1p3p"]
10 | params:
11 | - name: modbus
12 | choice: ["rs485", "tcpip"]
13 | id: 248
14 | render: |
15 | type: sungrow
16 | {{- include "modbus" . }}
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/tapo.yaml:
--------------------------------------------------------------------------------
1 | template: tapo
2 | products:
3 | - brand: TP-Link
4 | description:
5 | generic: Tapo P-Series Smart Plug
6 | group: switchsockets
7 | params:
8 | - name: host
9 | - name: user
10 | required: true
11 | - name: password
12 | required: true
13 | - preset: switchsocket
14 | render: |
15 | type: tapo
16 | uri: http://{{ .host }}
17 | user: {{ .user }}
18 | password: {{ .password }}
19 | {{ include "switchsocket" . }}
20 |
--------------------------------------------------------------------------------
/templates/definition/charger/tinkerforge-warp3-smart.yaml:
--------------------------------------------------------------------------------
1 | template: tinkerforge-warp3-smart
2 | products:
3 | - brand: TinkerForge
4 | description:
5 | generic: WARP3 Charger Smart
6 | capabilities: ["mA", "1p3p", "rfid"]
7 | requirements:
8 | evcc: ["skiptest"]
9 | params:
10 | - preset: mqtt
11 | - name: topic
12 | default: warp
13 | render: |
14 | type: warp2
15 | {{ include "mqtt" . }}
16 | topic: {{ .topic }}
17 | energymanager: {{ .topic }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/tplink.yaml:
--------------------------------------------------------------------------------
1 | template: tplink
2 | products:
3 | - brand: TP-Link
4 | description:
5 | generic: H-Series Smart Plug
6 | group: switchsockets
7 | params:
8 | - name: host
9 | - preset: switchsocket
10 | render: |
11 | type: tplink
12 | uri: {{ .host }}
13 | {{ include "switchsocket" . }}
14 |
--------------------------------------------------------------------------------
/templates/definition/charger/v2c.yaml:
--------------------------------------------------------------------------------
1 | template: v2c
2 | covers: ["trydan"]
3 | products:
4 | - brand: V2C
5 | description:
6 | generic: Trydan
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: host
11 | render: |
12 | type: trydan
13 | uri: http://{{ .host }}
14 |
--------------------------------------------------------------------------------
/templates/definition/charger/versicharge.yaml:
--------------------------------------------------------------------------------
1 | template: versicharge
2 | products:
3 | - brand: Siemens
4 | description:
5 | generic: Versicharge GEN3
6 | requirements:
7 | evcc: ["sponsorship"]
8 | description:
9 | de: Erfordert Firmware >= 2.135
10 | en: Requires firmware >= 2.135
11 | params:
12 | - name: modbus
13 | choice: ["tcpip"]
14 | id: 2
15 | render: |
16 | type: versicharge
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/charger/victron-evcs.yaml:
--------------------------------------------------------------------------------
1 | template: victron-evcs
2 | products:
3 | - brand: Victron
4 | description:
5 | generic: EV Charging Station
6 | requirements:
7 | description:
8 | en: Enter the host of the charger (not the GX device) and ensure that the charger is in manual mode.
9 | de: Trage den Host der Wallbox (nicht des GX-Geräts) ein und stelle sicher, dass die Wallbox sich im Modus "Manual" befindet.
10 | params:
11 | - name: modbus
12 | choice: ["tcpip"]
13 | id: 1
14 | render: |
15 | type: victron-evcs
16 | {{- include "modbus" . }}
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/victron.yaml:
--------------------------------------------------------------------------------
1 | template: victron
2 | products:
3 | - brand: Victron
4 | description:
5 | generic: EV Charging Station (via GX)
6 | requirements:
7 | description:
8 | en: Enter the host of the GX device (not the charger). The charger has to be in manual mode and Modbus has to be configured for ID 100.
9 | de: Trage den Host des GX-Gerätes (nicht der Wallbox) ein. Die Wallbox muss sich im Modus "Manual" befinden und Modbus ID 100 konfiguriert sein.
10 | params:
11 | - name: modbus
12 | choice: ["tcpip"]
13 | id: 100
14 | render: |
15 | type: victron
16 | {{- include "modbus" . }}
17 |
--------------------------------------------------------------------------------
/templates/definition/charger/wallbe-pre2019.yaml:
--------------------------------------------------------------------------------
1 | template: wallbe-pre2019
2 | deprecated: true
3 | products:
4 | - brand: Wallbe
5 | description:
6 | de: Eco (vor ~2019)
7 | en: Eco (pre ~2019)
8 | - brand: Wallbe
9 | description:
10 | de: Pro (vor ~2019)
11 | en: Pro (pre ~2019)
12 | requirements:
13 | description:
14 | en: DIP switch 10 must be set to 'ON'.
15 | de: Im Gerät muss der DIP Schalter 10 auf 'ON' gestellt sein.
16 | params:
17 | - name: host
18 | - name: port
19 | default: 502
20 | render: |
21 | type: wallbe
22 | uri: {{ .host }}:{{ .port }}
23 | legacy: true # set only for older Wallbe devices (pre ~2019, old controller firmware)
24 |
--------------------------------------------------------------------------------
/templates/definition/charger/wallbe.yaml:
--------------------------------------------------------------------------------
1 | template: wallbe
2 | deprecated: true
3 | products:
4 | - brand: Wallbe
5 | description:
6 | generic: Eco
7 | - brand: Wallbe
8 | description:
9 | generic: Pro
10 | requirements:
11 | description:
12 | en: The Wallbe must be connected using Ethernet and the DIP switch 10 must be set to 'ON'.
13 | de: Die Wallbox muss über ein Netzwerkkabel angebunden sein und im Gerät muss der DIP Schalter 10 auf 'ON' gestellt sein.
14 | params:
15 | - name: host
16 | - name: port
17 | default: 502
18 | render: |
19 | type: wallbe
20 | uri: {{ .host }}:{{ .port }}
21 |
--------------------------------------------------------------------------------
/templates/definition/charger/webasto-next.yaml:
--------------------------------------------------------------------------------
1 | template: webasto-next
2 | products:
3 | - brand: Ampure
4 | description:
5 | generic: NEXT
6 | - brand: Webasto
7 | description:
8 | generic: NEXT
9 | capabilities: ["rfid"]
10 | requirements:
11 | description:
12 | de: Modus "HEMS activated" muss aktiviert sein. RFID-Tags können durch evcc nur gelesen werden.
13 | en: Mode "HEMS activated" must be enabled. RFID tags can only be read by evcc.
14 | evcc: ["sponsorship"]
15 | params:
16 | - name: host
17 | - name: port
18 | default: 502
19 | render: |
20 | type: webasto-next
21 | uri: {{ .host }}:{{ .port }}
22 |
--------------------------------------------------------------------------------
/templates/definition/charger/weidmüller.yaml:
--------------------------------------------------------------------------------
1 | template: weidmüller
2 | products:
3 | - brand: Weidmüller
4 | description:
5 | generic: AC Smart
6 | capabilities: ["1p3p", "rfid"]
7 | requirements:
8 | evcc: ["sponsorship"]
9 | params:
10 | - name: host
11 | render: |
12 | type: weidmüller
13 | uri: {{ .host }}:502
14 |
--------------------------------------------------------------------------------
/templates/definition/embed.go:
--------------------------------------------------------------------------------
1 | package definition
2 |
3 | import "embed"
4 |
5 | //go:embed charger/*.yaml meter/*.yaml vehicle/*.yaml tariff/*.yaml
6 | var YamlTemplates embed.FS
7 |
--------------------------------------------------------------------------------
/templates/definition/meter/ac-elwa-2.yaml:
--------------------------------------------------------------------------------
1 | template: ac-elwa-2
2 | products:
3 | - brand: my-PV
4 | description:
5 | generic: AC ELWA 2
6 | params:
7 | - name: usage
8 | choice: ["aux"]
9 | - name: host
10 | - name: tempsource
11 | choice: ["1", "2"]
12 | default: "1"
13 | render: |
14 | type: custom
15 | power:
16 | source: http
17 | uri: http://{{ .host }}/data.jsn
18 | jq: .power_elwa2
19 | soc:
20 | source: http
21 | uri: http://{{ .host }}/data.jsn
22 | jq: .temp{{ .tempsource }}
23 | scale: 0.1
24 |
--------------------------------------------------------------------------------
/templates/definition/meter/ac-elwa-e.yaml:
--------------------------------------------------------------------------------
1 | template: ac-elwa-e
2 | covers: ["elwa-e"]
3 | products:
4 | - brand: my-PV
5 | description:
6 | generic: AC ELWA-E
7 | params:
8 | - name: usage
9 | choice: ["aux"]
10 | - name: host
11 | render: |
12 | type: custom
13 | power:
14 | source: http
15 | uri: http://{{ .host }}/data.jsn
16 | jq: .power
17 | soc:
18 | source: http
19 | uri: http://{{ .host }}/data.jsn
20 | jq: .temp1
21 | scale: 0.1
22 |
--------------------------------------------------------------------------------
/templates/definition/meter/apsystems-ez1.yaml:
--------------------------------------------------------------------------------
1 | template: apsystems-ez1
2 | products:
3 | - brand: APsystems
4 | description:
5 | generic: EZ1
6 | params:
7 | - name: usage
8 | choice: ["pv"]
9 | - name: host
10 | render: |
11 | type: custom
12 | {{- if eq .usage "pv" }}
13 | power:
14 | source: http
15 | uri: http://{{ .host }}:8050/getOutputData
16 | jq: .data.p1+.data.p2
17 | energy:
18 | source: http
19 | uri: http://{{ .host }}:8050/getOutputData
20 | jq: .data.te1+.data.te2
21 | {{- end }}
22 |
--------------------------------------------------------------------------------
/templates/definition/meter/be-mpm3pm.yaml:
--------------------------------------------------------------------------------
1 | template: mpm3pm
2 | products:
3 | - brand: Bernecker Engineering
4 | description:
5 | generic: MPM3PM
6 | params:
7 | - name: usage
8 | choice: ["grid", "charge"]
9 | - name: modbus
10 | choice: ["rs485"]
11 | render: |
12 | type: mbmd
13 | {{- include "modbus" . }}
14 | model: MPM
15 | power: Power
16 | energy: Import
17 | currents:
18 | - CurrentL1
19 | - CurrentL2
20 | - CurrentL3
21 | {{- if eq .usage "grid" }}
22 | powers:
23 | - PowerL1
24 | - PowerL2
25 | - PowerL3
26 | {{- end }}
27 | {{- if eq .usage "charge" }}
28 | voltages:
29 | - VoltageL1
30 | - VoltageL2
31 | - VoltageL3
32 | {{- end }}
33 |
--------------------------------------------------------------------------------
/templates/definition/meter/bosch-bpt.yaml:
--------------------------------------------------------------------------------
1 | template: bosch-bpt
2 | # UDP implementation is broken
3 | # deprecated: true
4 | products:
5 | - brand: Bosch
6 | description:
7 | generic: BPT-S 5 Hybrid
8 | requirements:
9 | evcc: ["skiptest"]
10 | params:
11 | - name: usage
12 | choice: ["grid", "pv", "battery"]
13 | allinone: true
14 | - name: uri
15 | - name: capacity
16 | advanced: true
17 | render: |
18 | type: bosch-bpt
19 | usage: {{ .usage }}
20 | uri: {{ .uri }}
21 | capacity: {{ .capacity }} # kWh
22 |
--------------------------------------------------------------------------------
/templates/definition/meter/cfos.yaml:
--------------------------------------------------------------------------------
1 | template: cfos
2 | products:
3 | - brand: cFos
4 | description:
5 | generic: PowerBrain Meter
6 | requirements:
7 | evcc: ["sponsorship"]
8 | params:
9 | - name: usage
10 | choice: ["charge"]
11 | - name: modbus
12 | choice: ["tcpip"]
13 | port: 4702
14 | id: 2
15 | render: |
16 | type: cfos
17 | {{- include "modbus" . }}
18 |
--------------------------------------------------------------------------------
/templates/definition/meter/discovergy.yaml:
--------------------------------------------------------------------------------
1 | template: discovergy
2 | products:
3 | - description:
4 | generic: Discovergy
5 | params:
6 | - name: usage
7 | choice: ["grid", "pv"]
8 | - name: user
9 | required: true
10 | - name: password
11 | required: true
12 | - name: meter
13 | required: true
14 | example: 1ESY1161229886
15 | render: |
16 | type: discovergy
17 | user: {{ .user }}
18 | password: {{ .password }} # password
19 | meter: {{ .meter }}
20 | {{- if eq .usage "pv" }}
21 | scale: -1
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/templates/definition/meter/dzg.yaml:
--------------------------------------------------------------------------------
1 | template: dzg
2 | products:
3 | - brand: DZG
4 | description:
5 | generic: DVH4013
6 | params:
7 | - name: usage
8 | choice: ["charge"]
9 | - name: modbus
10 | choice: ["rs485"]
11 | render: |
12 | type: mbmd
13 | {{- include "modbus" . }}
14 | model: dzg
15 | power: ImportPower
16 | energy: Import
17 | currents:
18 | - CurrentL1
19 | - CurrentL2
20 | - CurrentL3
21 | voltages:
22 | - VoltageL1
23 | - VoltageL2
24 | - VoltageL3
25 |
--------------------------------------------------------------------------------
/templates/definition/meter/eastron-sdm220_230.yaml:
--------------------------------------------------------------------------------
1 | template: eastron-sdm220_230
2 | products:
3 | - brand: Eastron
4 | description:
5 | generic: SDM220/230
6 | - brand: Weidmüller
7 | description:
8 | generic: EM110-RTU-2P
9 | - brand: Weidmüller
10 | description:
11 | generic: EM111-RTU-2P
12 | params:
13 | - name: usage
14 | choice: ["grid", "charge"]
15 | - name: modbus
16 | choice: ["rs485"]
17 | render: |
18 | type: mbmd
19 | {{- include "modbus" . }}
20 | model: sdm220
21 | power: Power
22 | energy: Import
23 |
--------------------------------------------------------------------------------
/templates/definition/meter/eastron-sdm72.yaml:
--------------------------------------------------------------------------------
1 | template: eastron-sdm72
2 | products:
3 | - brand: Eastron
4 | description:
5 | generic: SDM72D-M
6 | params:
7 | - name: usage
8 | choice: ["grid", "charge"]
9 | - name: modbus
10 | choice: ["rs485"]
11 | render: |
12 | type: mbmd
13 | {{- include "modbus" . }}
14 | model: sdm72
15 | power: Power
16 | energy: Import
17 |
--------------------------------------------------------------------------------
/templates/definition/meter/eebus-mgcp.yaml:
--------------------------------------------------------------------------------
1 | template: eebus-mgcp
2 | products:
3 | - description:
4 | de: "EEBus Netzanschlusspunkt"
5 | en: "EEBus Grid Connection Point"
6 | group: generic
7 | requirements:
8 | description:
9 | de: EEBus-Messstelle am Netzanschlusspunkt mit dem Use Case MGCP (Monitoring of Grid Connection Point).
10 | en: EEBus metering device at the grid connection point using use case MGCP (Monitoring of Grid Connection Point).
11 | evcc: ["eebus"]
12 | params:
13 | - name: usage
14 | choice: ["grid"]
15 | - preset: eebus
16 | render: |
17 | {{ include "eebus" . }}
18 | usage: {{ .usage }}
19 |
--------------------------------------------------------------------------------
/templates/definition/meter/fritzgrid.yaml:
--------------------------------------------------------------------------------
1 | template: fritzgrid
2 | products:
3 | - brand: "FRITZ!"
4 | description:
5 | generic: "FRITZ!Smart Energy 250"
6 | params:
7 | - name: usage
8 | choice: ["grid"]
9 | - name: uri
10 | default: https://fritz.box
11 | - name: user
12 | required: true
13 | - name: password
14 | required: true
15 | - name: ain
16 | required: true
17 | render: |
18 | type: fritzdect
19 | uri: {{ .uri }}
20 | user: {{ .user }}
21 | password: {{ .password }}
22 | ain: {{ .ain }} # switch actor identification number without blanks (see AIN number on switch sticker)
23 |
--------------------------------------------------------------------------------
/templates/definition/meter/goodwe-wifi.yaml:
--------------------------------------------------------------------------------
1 | template: goodwe-wifi
2 | # UDP implementation is broken
3 | # deprecated: true
4 | products:
5 | - brand: GoodWe
6 | description:
7 | generic: GoodWe over Wifi
8 | requirements:
9 | evcc: ["skiptest"]
10 | params:
11 | - name: usage
12 | choice: ["grid", "pv", "battery"]
13 | allinone: true
14 | - name: uri
15 | description:
16 | en: IP address or hostname
17 | de: IP-Adresse des Hostname
18 | render: |
19 | type: goodwe-wifi
20 | usage: {{ .usage }}
21 | uri: {{ .uri }}
22 |
--------------------------------------------------------------------------------
/templates/definition/meter/homewizard-kwh.yaml:
--------------------------------------------------------------------------------
1 | template: homewizard-kwh
2 | products:
3 | - brand: HomeWizard
4 | description:
5 | generic: kWh Meter
6 | params:
7 | - name: usage
8 | choice: ["pv", "charge"]
9 | - name: host
10 | render: |
11 | type: homewizard
12 | uri: http://{{ .host }}
13 | usage: {{ .usage }}
14 |
--------------------------------------------------------------------------------
/templates/definition/meter/homewizard-p1.yaml:
--------------------------------------------------------------------------------
1 | template: homewizard
2 | products:
3 | - brand: HomeWizard
4 | description:
5 | generic: Wi-Fi P1 Meter
6 | params:
7 | - name: usage
8 | choice: ["grid"]
9 | - name: host
10 | render: |
11 | type: homewizard
12 | uri: http://{{ .host }}
13 | usage: {{ .usage }}
14 |
--------------------------------------------------------------------------------
/templates/definition/meter/hoymiles-opendtu.yaml:
--------------------------------------------------------------------------------
1 | template: hoymiles-opendtu
2 | products:
3 | - brand: Hoymiles
4 | description:
5 | generic: HM & HMS Series (via OpenDTU)
6 | params:
7 | - name: usage
8 | choice: ["pv"]
9 | - name: host
10 | render: |
11 | type: custom
12 | power:
13 | source: http
14 | uri: http://{{ .host }}/api/livedata/status
15 | jq: .total.Power.v
16 | energy:
17 | source: http
18 | uri: http://{{ .host }}/api/livedata/status
19 | jq: .total.YieldTotal.v
20 |
--------------------------------------------------------------------------------
/templates/definition/meter/inepro.yaml:
--------------------------------------------------------------------------------
1 | template: inepro
2 | products:
3 | - brand: inepro
4 | description:
5 | generic: PRO380-MOD
6 | params:
7 | - name: usage
8 | choice: ["grid", "charge"]
9 | - name: modbus
10 | choice: ["rs485"]
11 | render: |
12 | type: mbmd
13 | {{- include "modbus" . }}
14 | model: inepro
15 | power: Power
16 | energy: Import
17 | currents:
18 | - CurrentL1
19 | - CurrentL2
20 | - CurrentL3
21 | {{- if eq .usage "grid" }}
22 | powers:
23 | - PowerL1
24 | - PowerL2
25 | - PowerL3
26 | {{- end }}
27 | {{- if eq .usage "charge" }}
28 | voltages:
29 | - VoltageL1
30 | - VoltageL2
31 | - VoltageL3
32 | {{- end }}
33 |
--------------------------------------------------------------------------------
/templates/definition/meter/iometer.yaml:
--------------------------------------------------------------------------------
1 | template: iometer
2 | products:
3 | - brand: IOmeter
4 | params:
5 | - name: usage
6 | choice: ["grid"]
7 | - name: host
8 | description:
9 | de: IP deines IOmeter
10 | en: IP of your IOmeter
11 | render: |
12 | type: custom
13 | power:
14 | source: http
15 | uri: http://{{ .host }}/v1/reading
16 | method: GET
17 | jq: .meter.reading.registers[] | select(.obis == "01-00:10.07.00*ff") | .value
18 | cache: 10s
19 | energy:
20 | source: http
21 | uri: http://{{ .host }}/v1/reading
22 | method: GET
23 | jq: (.meter.reading.registers[] | select(.obis == "01-00:01.08.00*ff") | .value) / 1000
24 | cache: 10s
25 |
--------------------------------------------------------------------------------
/templates/definition/meter/kostal-piko-legacy.yaml:
--------------------------------------------------------------------------------
1 | template: kostal-piko-legacy
2 | products:
3 | - brand: Kostal
4 | description:
5 | generic: Piko (legacy)
6 | params:
7 | - name: usage
8 | choice: ["pv"]
9 | - name: host
10 | - name: user
11 | required: true
12 | - name: password
13 | required: true
14 | render: |
15 | type: custom
16 | power:
17 | {{- if eq .usage "pv" }}
18 | source: http
19 | uri: http://{{ .host }}
20 | auth:
21 | type: basic
22 | user: {{ .user }}
23 | password: {{ .password }}
24 | regex: '(?s)aktuell</td>\s+<td[^>]+>\s+(\d+)</td>'
25 | default: 0
26 | {{- end }}
27 |
--------------------------------------------------------------------------------
/templates/definition/meter/mystrom.yaml:
--------------------------------------------------------------------------------
1 | template: mystrom
2 | products:
3 | - brand: myStrom
4 | description:
5 | generic: Switch
6 | group: switchsockets
7 | params:
8 | - name: usage
9 | choice: ["pv", "charge"]
10 | - name: host
11 | render: |
12 | type: mystrom
13 | uri: http://{{ .host }}
14 |
--------------------------------------------------------------------------------
/templates/definition/meter/shelly-pro-3em.yaml:
--------------------------------------------------------------------------------
1 | template: shelly-pro-3em
2 | products:
3 | - brand: Shelly
4 | description:
5 | generic: Pro 3 EM
6 | params:
7 | - name: usage
8 | choice: ["grid", "pv", "charge"]
9 | - name: host
10 | - name: user
11 | - name: password
12 | render: |
13 | type: shelly
14 | uri: http://{{ .host }} # shelly device ip address (local)
15 | {{- if .user }}
16 | user: {{ .user }}
17 | {{- end }}
18 | {{- if .password }}
19 | password: {{ .password }}
20 | {{- end }}
21 | usage: {{ .usage }}
22 | channel: 0 # shelly device relay channel
23 |
--------------------------------------------------------------------------------
/templates/definition/meter/sma-energymeter.yaml:
--------------------------------------------------------------------------------
1 | template: sma-energy-meter
2 | products:
3 | - brand: SMA
4 | description:
5 | generic: Energy Meter
6 | params:
7 | - name: usage
8 | choice: ["grid", "pv"]
9 | - name: host
10 | - name: interface
11 | render: |
12 | type: sma
13 | uri: {{ .host }}
14 | {{- if .interface }}
15 | interface: {{ .interface }}
16 | {{- end }}
17 | {{- if eq .usage "pv" }}
18 | scale: -1
19 | {{- end }}
20 |
--------------------------------------------------------------------------------
/templates/definition/meter/solaranzeige-mqtt.yaml:
--------------------------------------------------------------------------------
1 | template: solaranzeige
2 | products:
3 | - brand: Solaranzeige
4 | description:
5 | generic: Solaranzeige
6 | requirements:
7 | evcc: ["skiptest"]
8 | params:
9 | - name: usage
10 | choice: ["grid", "pv"]
11 | - preset: mqtt
12 | - name: topic
13 | default: solaranzeige/box1
14 | render: |
15 | type: custom
16 | power:
17 | source: mqtt
18 | {{- include "mqtt" . | indent 2 }}
19 | {{- if eq .usage "grid" }}
20 | topic: {{ .topic }}/einspeisung_bezug
21 | scale: -1
22 | {{- end }}
23 | {{- if eq .usage "pv" }}
24 | topic: {{ .topic }}/pv_leistung
25 | {{- end }}
26 |
--------------------------------------------------------------------------------
/templates/definition/meter/solarmax-inverter-smt.yaml:
--------------------------------------------------------------------------------
1 | template: solarmax-smt
2 | products:
3 | - brand: SolarMax
4 | description:
5 | generic: SolarMax SMT
6 | params:
7 | - name: usage
8 | choice: ["pv"]
9 | - name: modbus
10 | choice: ["tcpip"]
11 | id: 1
12 | render: |
13 | type: custom
14 | power:
15 | source: modbus
16 | {{- include "modbus" . | indent 2 }}
17 | register:
18 | address: 4151 # PAC
19 | type: holding
20 | decode: uint32
21 | scale: 0.1
22 | energy:
23 | source: modbus
24 | {{- include "modbus" . | indent 2 }}
25 | register:
26 | address: 4129 # Total
27 | type: holding
28 | decode: uint32
29 |
--------------------------------------------------------------------------------
/templates/definition/meter/tapo.yaml:
--------------------------------------------------------------------------------
1 | template: tapo
2 | products:
3 | - brand: TP-Link
4 | description:
5 | generic: Tapo P-Series Smart Plug
6 | group: switchsockets
7 | params:
8 | - name: usage
9 | choice: ["pv"]
10 | - name: host
11 | - name: user
12 | required: true
13 | - name: password
14 | required: true
15 | render: |
16 | type: tapo
17 | uri: http://{{ .host }}
18 | user: {{ .user }}
19 | password: {{ .password }}
20 |
--------------------------------------------------------------------------------
/templates/definition/meter/thor.yaml:
--------------------------------------------------------------------------------
1 | template: thor
2 | products:
3 | - brand: my-PV
4 | description:
5 | generic: AC•THOR
6 | params:
7 | - name: usage
8 | choice: ["aux"]
9 | - name: host
10 | render: |
11 | type: custom
12 | power:
13 | source: http
14 | uri: http://{{ .host }}/data.jsn
15 | jq: if .power_act == null then 0 else .power_act end + if .power_ac9 == null then 0 else .power_ac9 end
16 |
--------------------------------------------------------------------------------
/templates/definition/meter/tibber-pulse.yaml:
--------------------------------------------------------------------------------
1 | template: tibber-pulse
2 | products:
3 | - brand: Tibber
4 | description:
5 | generic: Pulse
6 | requirements:
7 | evcc: ["skiptest"]
8 | params:
9 | - name: usage
10 | choice: ["grid"]
11 | - name: token
12 | mask: true
13 | required: true
14 | example: 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE
15 | - name: homeid
16 | description:
17 | generic: Home ID
18 | example: 96a14971-525a-4420-aae9-e5aedaa129ff
19 | - name: timeout
20 | deprecated: true
21 | render: |
22 | type: tibber-pulse
23 | token: {{ .token }}
24 | homeid: {{ .homeid }}
25 |
--------------------------------------------------------------------------------
/templates/definition/meter/tplink.yaml:
--------------------------------------------------------------------------------
1 | template: tplink
2 | products:
3 | - brand: TP-Link
4 | description:
5 | generic: H-Series Smart Plug
6 | group: switchsockets
7 | params:
8 | - name: usage
9 | choice: ["pv"]
10 | - name: host
11 | render: |
12 | type: tplink
13 | uri: {{ .host }}
14 |
--------------------------------------------------------------------------------
/templates/definition/meter/tq-em.yaml:
--------------------------------------------------------------------------------
1 | template: tq-em
2 | products:
3 | - brand: TQ
4 | description:
5 | generic: Energy Manager EM2xx/EM3xx
6 | params:
7 | - name: usage
8 | choice: ["grid"]
9 | - name: host
10 | - name: port
11 | default: 80
12 | - name: password
13 | render: |
14 | type: tq-em
15 | uri: http://{{ .host }}:{{ .port }}
16 | password: {{ .password }}
17 |
--------------------------------------------------------------------------------
/templates/definition/meter/volkszaehler-ws.yaml:
--------------------------------------------------------------------------------
1 | template: volkszaehler-ws
2 | products:
3 | - brand: Volkszähler
4 | description:
5 | generic: WebSocket API
6 | requirements:
7 | evcc: ["skiptest"]
8 | group: generic
9 | params:
10 | - name: usage
11 | choice: ["grid"]
12 | - name: host
13 | - name: port
14 | default: 8082
15 | - name: uuid
16 | required: true
17 | render: |
18 | type: custom
19 | power: # power reading
20 | source: ws # use websocket plugin
21 | uri: ws://{{ .host }}:{{ .port }}/socket
22 | jq: .data | select(.uuid=="{{ unquote .uuid }}") .tuples[0][1] # parse response json
23 | timeout: 30s
24 | scale: 1
25 |
--------------------------------------------------------------------------------
/templates/definition/meter/wago-879-30xx.yaml:
--------------------------------------------------------------------------------
1 | template: wago-879-30xx
2 | products:
3 | - brand: Wago
4 | description:
5 | generic: 879-30xx
6 | params:
7 | - name: usage
8 | choice: ["grid", "charge"]
9 | - name: modbus
10 | choice: ["rs485"]
11 | render: |
12 | type: mbmd
13 | {{- include "modbus" . }}
14 | model: wago87930
15 | power: Power
16 | energy: Import
17 | currents:
18 | - CurrentL1
19 | - CurrentL2
20 | - CurrentL3
21 | powers:
22 | - PowerL1
23 | - PowerL2
24 | - PowerL3
25 | voltages:
26 | - VoltageL1
27 | - VoltageL2
28 | - VoltageL3
29 |
--------------------------------------------------------------------------------
/templates/definition/tariff/allinpower.yaml:
--------------------------------------------------------------------------------
1 | template: allinpower
2 | deprecated: true
3 | products:
4 | - brand: All in Power
5 | requirements:
6 | evcc: ["skiptest"]
7 | group: price
8 | countries: ["NL"]
9 | params:
10 | - preset: tariff-base
11 | render: |
12 | type: custom
13 | {{ include "tariff-base" . }}
14 | forecast:
15 | source: http
16 | uri: https://api.allinpower.nl/troodon/api/p/spot_market/prices/?product_type=ELK
17 | jq: |
18 | [.timestamps, .prices] | transpose | map({
19 | "start": .[0] | strptime("%FT%T.%f%z") | strftime("%FT%TZ"),
20 | "end": .[0] | strptime("%FT%T.%f%z") | mktime + 3600 | strftime("%FT%TZ"),
21 | "value": .[1]
22 | }) | tostring
23 |
--------------------------------------------------------------------------------
/templates/definition/tariff/amber.yaml:
--------------------------------------------------------------------------------
1 | template: amber
2 | products:
3 | - brand: Amber Electric
4 | group: price
5 | countries: ["AU"]
6 | params:
7 | - name: token
8 | required: true
9 | - name: siteid
10 | description:
11 | generic: Site ID
12 | required: true
13 | - name: channel
14 | type: choice
15 | choice: ["general", "feedIn", "controlledLoad"]
16 | required: true
17 | - preset: tariff-base
18 | render: |
19 | type: amber
20 | token: {{ .token }}
21 | siteid: {{ .siteid }}
22 | channel: {{ .channel }}
23 | {{ include "tariff-base" . }}
24 |
--------------------------------------------------------------------------------
/templates/definition/tariff/awattar.yaml:
--------------------------------------------------------------------------------
1 | template: awattar
2 | products:
3 | - brand: Awattar
4 | group: price
5 | countries: ["DE", "AT"]
6 | params:
7 | - name: region
8 | example: AT
9 | type: choice
10 | choice: ["DE", "AT"]
11 | required: true
12 | - preset: tariff-base
13 | render: |
14 | type: awattar
15 | region: {{ .region }}
16 | {{ include "tariff-base" . }}
17 |
--------------------------------------------------------------------------------
/templates/definition/tariff/elering.yaml:
--------------------------------------------------------------------------------
1 | template: elering
2 | deprecated: true
3 | products:
4 | - brand: Nordpool
5 | description:
6 | generic: "Elering"
7 | requirements:
8 | evcc: ["skiptest"]
9 | group: price
10 | countries: ["EE", "LT", "LV", "FI"]
11 | params:
12 | - name: region
13 | example: ee
14 | type: choice
15 | choice: ["ee", "lt", "lv", "fi"]
16 | required: true
17 | - preset: tariff-base
18 | render: |
19 | type: elering
20 | region: {{ .region }}
21 | {{ include "tariff-base" . }}
22 |
--------------------------------------------------------------------------------
/templates/definition/tariff/energinet.yaml:
--------------------------------------------------------------------------------
1 | template: energinet
2 | deprecated: true
3 | products:
4 | - brand: Energinet
5 | requirements:
6 | evcc: ["skiptest"]
7 | group: price
8 | countries: ["DK"]
9 | params:
10 | - name: region
11 | example: dk1
12 | type: choice
13 | choice: ["dk1", "dk2"]
14 | required: true
15 | - preset: tariff-base
16 | render: |
17 | type: energinet
18 | region: {{ .region }}
19 | {{ include "tariff-base" . }}
20 |
--------------------------------------------------------------------------------
/templates/definition/tariff/groupe-e.yaml:
--------------------------------------------------------------------------------
1 | template: groupe-e
2 | products:
3 | - brand: Groupe E
4 | description:
5 | generic: Vario Plus
6 | requirements:
7 | evcc: ["skiptest"]
8 | group: price
9 | countries: ["CH"]
10 | params:
11 | - preset: tariff-base
12 | render: |
13 | type: groupe-e
14 | {{ include "tariff-base" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/tariff/gruenstromindex.yaml:
--------------------------------------------------------------------------------
1 | template: grünstromindex
2 | products:
3 | - brand: Grünstromindex
4 | requirements:
5 | description:
6 | de: "Regionale Emissionsdaten von https://gruenstromindex.de"
7 | en: "Regional emission data from https://gruenstromindex.de"
8 | evcc: ["skiptest"]
9 | group: co2
10 | countries: ["DE"]
11 | params:
12 | - name: zip
13 | required: true
14 | - name: token
15 | help:
16 | de: "Token für den Zugriff auf die API von https://console.corrently.io/"
17 | en: "Token for accessing the API from https://console.corrently.io"
18 | render: |
19 | type: grünstromindex
20 | features: ["cacheable"]
21 | zip: {{ .zip }}
22 | token: {{ .token }}
23 |
--------------------------------------------------------------------------------
/templates/definition/tariff/pun.yaml:
--------------------------------------------------------------------------------
1 | template: pun
2 | products:
3 | - brand: PUN Orario
4 | requirements:
5 | evcc: ["skiptest"]
6 | description:
7 | de: "Preisdaten von https://www.mercatoelettrico.org/it/. Wird oft zur Einspeisung ins Netz verwendet."
8 | en: "Price data from https://www.mercatoelettrico.org/it/. Often used for feeding into the grid."
9 | group: price
10 | countries: ["IT"]
11 | params:
12 | - preset: tariff-base
13 | render: |
14 | type: pun
15 | {{ include "tariff-base" . }}
16 |
--------------------------------------------------------------------------------
/templates/definition/tariff/smartenergy.yaml:
--------------------------------------------------------------------------------
1 | template: smartenergy
2 | products:
3 | - brand: SmartEnergy
4 | description:
5 | generic: smartCONTROL
6 | group: price
7 | countries: ["AT"]
8 | requirements:
9 | evcc: ["skiptest"]
10 | params:
11 | - preset: tariff-base
12 | render: |
13 | type: smartenergy
14 | {{ include "tariff-base" . }}
15 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/aiways.yaml:
--------------------------------------------------------------------------------
1 | template: aiways
2 | products:
3 | - brand: Aiways
4 | params:
5 | - preset: vehicle-base
6 | - name: vin
7 | required: true
8 | render: |
9 | type: aiways
10 | {{ include "vehicle-base" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/audi.yaml:
--------------------------------------------------------------------------------
1 | template: audi
2 | covers: ["etron"]
3 | products:
4 | - brand: Audi
5 | params:
6 | - preset: vehicle-base
7 | - name: vin
8 | example: WAUZZZ...
9 | render: |
10 | type: etron
11 | {{ include "vehicle-base" . }}
12 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/carwings.yaml:
--------------------------------------------------------------------------------
1 | template: carwings
2 | products:
3 | - brand: Nissan
4 | description:
5 | generic: Leaf (pre 2019)
6 | params:
7 | - preset: vehicle-base
8 | render: |
9 | type: carwings
10 | {{ include "vehicle-base" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/dacia.yaml:
--------------------------------------------------------------------------------
1 | template: dacia
2 | products:
3 | - brand: Dacia
4 | params:
5 | - preset: vehicle-base
6 | - preset: vehicle-features
7 | render: |
8 | type: dacia
9 | {{ include "vehicle-base" . }}
10 | {{ include "vehicle-features" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/ford.yaml:
--------------------------------------------------------------------------------
1 | template: ford
2 | deprecated: true
3 | products:
4 | - brand: Ford
5 | requirements:
6 | description:
7 | de: "Hinweis: Ford hat kürzlich den API-Zugriff für seine Benutzer deaktiviert."
8 | en: "Note: Ford has recently disabled API-access for their users."
9 | params:
10 | - preset: vehicle-base
11 | - name: vin
12 | example: WF0FXX...
13 | - name: domain
14 | type: choice
15 | choice: ["com", "de"]
16 | default: com
17 | required: true
18 | render: |
19 | type: ford
20 | {{ include "vehicle-base" . }}
21 | domain: {{ .domain }}
22 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/jaguar-landrover.yaml:
--------------------------------------------------------------------------------
1 | template: jaguar-landrover
2 | deprecated: true
3 | products:
4 | - brand: Jaguar
5 | - brand: Land Rover
6 | params:
7 | - preset: vehicle-base
8 | render: |
9 | type: jaguar
10 | {{ include "vehicle-base" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/mg.yaml:
--------------------------------------------------------------------------------
1 | template: mg
2 | products:
3 | - brand: MG
4 | params:
5 | - preset: vehicle-base
6 | - name: vin
7 | required: true
8 | - name: region
9 | description:
10 | de: Region
11 | en: Region
12 | type: choice
13 | choice: ["EU", "AU"]
14 | default: EU
15 | required: true
16 | advanced: true
17 | render: |
18 | type: mg
19 | {{ include "vehicle-base" . }}
20 | region: {{ .region }}
21 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/nissan-ariya.yaml:
--------------------------------------------------------------------------------
1 | template: nissan-ariya
2 | products:
3 | - brand: Nissan
4 | description:
5 | generic: Ariya
6 | params:
7 | - preset: vehicle-base
8 | render: |
9 | type: nissan
10 | version: v2
11 | {{ include "vehicle-base" . }}
12 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/nissan.yaml:
--------------------------------------------------------------------------------
1 | template: nissan
2 | products:
3 | - brand: Nissan
4 | description:
5 | generic: Leaf
6 | params:
7 | - preset: vehicle-base
8 | render: |
9 | type: nissan
10 | {{ include "vehicle-base" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/offline.yaml:
--------------------------------------------------------------------------------
1 | template: offline
2 | products:
3 | - description:
4 | en: Generic vehicle (without API)
5 | de: Generisches Fahrzeug (ohne API)
6 | requirements:
7 | description:
8 | de:
9 | group: generic
10 | params:
11 | - preset: vehicle-common
12 | - name: coarsecurrent
13 | advanced: true
14 | - name: welcomecharge
15 | advanced: true
16 | render: |
17 | type: custom
18 | {{- include "vehicle-common" . }}
19 | features:
20 | - offline
21 | {{- if eq .coarsecurrent "true" }}
22 | - coarsecurrent
23 | {{- end }}
24 | {{- if eq .welcomecharge "true" }}
25 | - welcomecharge
26 | {{- end }}
27 | soc:
28 | source: const
29 | value: 0
30 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/polestar.yaml:
--------------------------------------------------------------------------------
1 | template: polestar
2 | products:
3 | - brand: Polestar
4 | requirements:
5 | evcc: ["skiptest"]
6 | params:
7 | - preset: vehicle-base
8 | - name: vin
9 | example: LPSVS...
10 | render: |
11 | type: polestar
12 | {{ include "vehicle-base" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/porsche.yaml:
--------------------------------------------------------------------------------
1 | template: porsche
2 | deprecated: true
3 | products:
4 | - brand: Porsche
5 | params:
6 | - preset: vehicle-base
7 | render: |
8 | type: porsche
9 | {{ include "vehicle-base" . }}
10 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/seat-cupra.yaml:
--------------------------------------------------------------------------------
1 | template: cupra
2 | products:
3 | - brand: Seat
4 | description:
5 | generic: CupraConnect Gen4 (Born, Formentor, Tavascan)
6 | params:
7 | - preset: vehicle-base
8 | - preset: vehicle-features
9 | render: |
10 | type: cupra
11 | {{ include "vehicle-base" . }}
12 | {{ include "vehicle-features" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/seat.yaml:
--------------------------------------------------------------------------------
1 | template: seat
2 | products:
3 | - brand: Seat
4 | description:
5 | generic: CupraConnect Gen3 (Ateca, Leon, Formentor, Tarraco)
6 | params:
7 | - preset: vehicle-base
8 | render: |
9 | type: seat
10 | {{ include "vehicle-base" . }}
11 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/skoda.yaml:
--------------------------------------------------------------------------------
1 | template: skoda
2 | covers: ["enyaq"]
3 | products:
4 | - brand: Skoda
5 | params:
6 | - preset: vehicle-base
7 | - name: timeout
8 | render: |
9 | type: skoda
10 | {{ include "vehicle-base" . }}
11 | timeout: {{ .timeout }}
12 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/smart-hello.yaml:
--------------------------------------------------------------------------------
1 | template: smart-hello
2 | products:
3 | - brand: Smart
4 | description:
5 | generic: "#1"
6 | params:
7 | - preset: vehicle-base
8 | - preset: vehicle-features
9 | render: |
10 | type: smart-hello
11 | {{ include "vehicle-base" . }}
12 | {{ include "vehicle-features" . }}
13 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/toyota.yaml:
--------------------------------------------------------------------------------
1 | template: toyota
2 | products:
3 | - brand: Toyota
4 | requirements:
5 | description:
6 | de: |
7 | Benötigt Toyota Connected Services Account.
8 | en: |
9 | Requires Toyota Connected Services Account.
10 | params:
11 | - preset: vehicle-common
12 | - name: user
13 | required: true
14 | - name: password
15 | required: true
16 | - name: vin
17 | example: JT...
18 | - name: cache
19 | default: 15m
20 | render: |
21 | type: toyota
22 | {{ include "vehicle-common" . }}
23 | user: {{ .user }}
24 | password: {{ .password }}
25 | vin: {{ .vin }}
26 | cache: {{ .cache }}
27 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/vw.yaml:
--------------------------------------------------------------------------------
1 | template: vw
2 | covers: ["id"]
3 | products:
4 | - brand: Volkswagen
5 | description:
6 | generic: We Connect ID
7 | requirements:
8 | description:
9 | de: e-Golf, e-Up, ID Familie
10 | en: e-Golf, e-Up, ID family
11 | params:
12 | - preset: vehicle-base
13 | - name: vin
14 | example: WVWZZZ...
15 | - name: timeout
16 | default: 10s
17 | - preset: vehicle-features
18 | render: |
19 | type: vw
20 | {{ include "vehicle-base" . }}
21 | {{ include "vehicle-features" . }}
22 | timeout: {{ .timeout }}
23 |
--------------------------------------------------------------------------------
/templates/definition/vehicle/zero.yaml:
--------------------------------------------------------------------------------
1 | template: zero
2 | products:
3 | - brand: Zero Motorcycles
4 | params:
5 | - preset: vehicle-base
6 | render: |
7 | type: zero
8 | {{ include "vehicle-base" . }}
9 |
--------------------------------------------------------------------------------
/templates/evcc.io/.gitignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/tests/config-circuit.evcc.yaml:
--------------------------------------------------------------------------------
1 | site:
2 | meters:
3 | grid: grid
4 |
5 | meters:
6 | - name: grid
7 | type: template
8 | template: demo-meter
9 | power: 2070
10 | currentL1: 3
11 | currentL2: 3
12 | currentL3: 3
13 |
14 | loadpoints:
15 | - title: Carport
16 | charger: charger
17 | circuit: main
18 |
19 | circuits:
20 | - name: main
21 | meter: grid
22 | maxcurrent: 16
23 |
24 | chargers:
25 | - name: charger
26 | type: template
27 | template: demo-charger
28 | status: C
29 | enabled: true
30 | power: 1000
31 |
--------------------------------------------------------------------------------
/tests/config-empty.evcc.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evcc-io/evcc/2291b371e343043037ebef13c143a8c633eeb853/tests/config-empty.evcc.yaml
--------------------------------------------------------------------------------
/tests/config-grid-only.evcc.yaml:
--------------------------------------------------------------------------------
1 | site:
2 | title: Hello World
3 | meters:
4 | grid: grid
5 |
6 | meters:
7 | - name: grid
8 | type: custom
9 | power:
10 | source: js
11 | script: |
12 | 1000
13 |
14 | loadpoints:
15 | - title: Carport
16 | charger: charger
17 |
18 | chargers:
19 | - name: charger
20 | type: custom
21 | enable:
22 | source: js
23 | script:
24 | enabled:
25 | source: js
26 | script: |
27 | false
28 | status:
29 | source: js
30 | script: |
31 | "B"
32 | maxcurrent:
33 | source: js
34 | script:
35 |
--------------------------------------------------------------------------------
/tests/config-invalid-template.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE `configs` (
4 | `id` integer PRIMARY KEY AUTOINCREMENT
5 | , `class` integer
6 | , `type` text
7 | , `title` text
8 | , `icon` text
9 | , `product` text
10 | , `value` text
11 | );
12 | CREATE TABLE `settings` (
13 | `key` text
14 | , `value` text
15 | , PRIMARY KEY(`key`)
16 | );
17 |
18 | INSERT INTO configs(id, class, type, title, icon, product, value) VALUES(1, 2, 'template', '', '', 'Demo meter', '{"maxacpower":"0","power_old":222,"template":"demo-meter","usage":"grid"}');
19 | INSERT INTO settings("key", value) VALUES('gridMeter', 'db:1');
20 |
21 | COMMIT;
--------------------------------------------------------------------------------
/tests/config-one-lp.evcc.yaml:
--------------------------------------------------------------------------------
1 | site:
2 | title: Hello World
3 |
4 | loadpoints:
5 | - title: Carport
6 | charger: charger
7 |
8 | chargers:
9 | - name: charger
10 | type: custom
11 | enable:
12 | source: js
13 | script:
14 | enabled:
15 | source: js
16 | script: |
17 | false
18 | status:
19 | source: js
20 | script: |
21 | "B"
22 | maxcurrent:
23 | source: js
24 | script:
25 |
--------------------------------------------------------------------------------
/tests/custom-css.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --evcc-dark-green: rebeccapurple;
3 | --evcc-yellow: hotpink;
4 | }
5 |
6 | [data-testid="header"],
7 | [data-testid="footer"] {
8 | display: none !important;
9 | }
10 |
11 | /* test against markup injection */
12 | </style><script>alert("hello");</script><style>
--------------------------------------------------------------------------------
/tests/fast.evcc.yaml:
--------------------------------------------------------------------------------
1 | interval: 0.1s
2 |
--------------------------------------------------------------------------------
/tests/fatal-db.evcc.yaml:
--------------------------------------------------------------------------------
1 | site:
2 | title: Hello World
3 |
4 | database:
5 | type: sqliteInvalid
6 | dsn: /path/to/db
7 |
--------------------------------------------------------------------------------
/tests/fatal-syntax.evcc.yaml:
--------------------------------------------------------------------------------
1 | s!ite:
2 | title: Hello World
3 |
--------------------------------------------------------------------------------
/tests/issue.evcc.yaml:
--------------------------------------------------------------------------------
1 | interval: 10s
2 |
3 | site:
4 | title: Hello World
5 | meters:
6 | pv: carport_pv
7 |
8 | loadpoints:
9 | - title: Carport
10 | charger: charger
11 |
12 | chargers:
13 | - name: charger
14 | type: template
15 | template: demo-charger
16 |
17 | meters:
18 | - name: carport_pv
19 | type: template
20 | template: demo-meter
21 | power: 1234
22 |
--------------------------------------------------------------------------------
/tests/loadpoint-sort.evcc.yaml:
--------------------------------------------------------------------------------
1 | site:
2 | title: Loadpoint Sort Test
3 |
4 | meters:
5 | - name: grid
6 | type: template
7 | template: demo-meter
8 | power: 200
9 |
10 | loadpoints:
11 | - title: First Loadpoint
12 | charger: charger_1
13 | mode: now
14 | - title: Second Loadpoint
15 | charger: charger_2
16 | mode: now
17 | - title: Third Loadpoint
18 | charger: charger_3
19 | mode: now
20 |
21 | chargers:
22 | - name: charger_1
23 | type: template
24 | template: demo-charger
25 | - name: charger_2
26 | type: template
27 | template: demo-charger
28 | - name: charger_3
29 | type: template
30 | template: demo-charger
31 |
--------------------------------------------------------------------------------
/tests/mqtt.ts:
--------------------------------------------------------------------------------
1 | import mqtt from "mqtt";
2 |
3 | export async function isMqttReachable(
4 | broker: string,
5 | username: string,
6 | password: string
7 | ): Promise<boolean> {
8 | try {
9 | const client = mqtt.connect(`mqtt://${broker}`, {
10 | connectTimeout: 2000,
11 | username,
12 | password,
13 | });
14 |
15 | await new Promise<void>((resolve, reject) => {
16 | client.once("connect", () => resolve());
17 | client.once("error", (err) => reject(err));
18 | });
19 |
20 | client.end();
21 | return true; // connection successful
22 | } catch {
23 | return false; // connection failed
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/password.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS `settings` (
2 | `key` text
3 | , `value` text
4 | , PRIMARY KEY(`key`)
5 | );
6 |
7 | -- password: secret
8 | INSERT INTO settings("key", value) VALUES('adminPassword', '$2a$10$HNLoqiTO5oLwopczA/wcPOebfO79S.hnAA5HOkx5p6o3g5a2E30v2');
9 |
--------------------------------------------------------------------------------
/tests/plan-fixed-tariff.evcc.yaml:
--------------------------------------------------------------------------------
1 | interval: 0.1s
2 |
3 | loadpoints:
4 | - title: Loadpoint
5 | charger: charger
6 |
7 | chargers:
8 | - name: charger
9 | type: custom
10 | enable:
11 | source: js
12 | script:
13 | enabled:
14 | source: js
15 | script: |
16 | false
17 | status:
18 | source: js
19 | script: |
20 | "B"
21 | maxcurrent:
22 | source: js
23 | script:
24 |
25 | tariffs:
26 | currency: EUR
27 | grid:
28 | type: fixed
29 | price: 0.4 # EUR/kWh
30 |
--------------------------------------------------------------------------------
/tests/sessions.evcc.yaml:
--------------------------------------------------------------------------------
1 | interval: 0.1s
2 |
3 | site:
4 | title: Sessions
5 | meters:
6 | grid: grid
7 |
8 | meters:
9 | - name: grid
10 | type: template
11 | template: demo-meter
12 | power: 200
13 |
14 | loadpoints:
15 | - title: Carport
16 | charger: charger1
17 | vehicle: tesla
18 | - title: Garage
19 | charger: charger2
20 | vehicle: egolf
21 |
22 | vehicles:
23 | - name: tesla
24 | title: weißes Model 3
25 | - name: egolf
26 | title: blauer e-Golf
27 |
28 | chargers:
29 | - name: charger1
30 | type: template
31 | template: demo-charger
32 | status: B
33 | - name: charger2
34 | type: template
35 | template: demo-charger
36 | status: B
37 |
--------------------------------------------------------------------------------
/tests/simulator/index.html:
--------------------------------------------------------------------------------
1 | <!doctype html>
2 | <html lang="en">
3 | <head>
4 | <meta charset="UTF-8" />
5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 | <title>evcc Simulator</title>
7 | </head>
8 |
9 | <body>
10 | <div id="app"></div>
11 | <script type="module" src="./src/main.js"></script>
12 | </body>
13 | </html>
14 |
--------------------------------------------------------------------------------
/tests/simulator/src/main.ts:
--------------------------------------------------------------------------------
1 | import "bootstrap/dist/css/bootstrap.min.css";
2 | import "../../../assets/css/app.css";
3 | import { createApp } from "vue";
4 | import Simulator from "./Simulator.vue";
5 |
6 | createApp(Simulator).mount("#app");
7 |
--------------------------------------------------------------------------------
/tests/simulator/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import vue from "@vitejs/plugin-vue";
3 | import api from "./api";
4 |
5 | export default defineConfig({
6 | plugins: [
7 | vue(),
8 | // @ts-expect-error the in api() declared 'enforce' property is not supported in TypeScript but in JavaScript
9 | api(),
10 | ],
11 | server: { port: 7072, host: true },
12 | });
13 |
--------------------------------------------------------------------------------
/tests/sponsor.evcc.yaml:
--------------------------------------------------------------------------------
1 | # expired sponsor token
2 | sponsortoken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJldmNjLmlvIiwic3ViIjoidHJpYWwiLCJleHAiOjE3NTQ5OTI4MDAsImlhdCI6MTc1MzY5NjgwMCwic3BlIjp0cnVlLCJzcmMiOiJtYSJ9.XKa5DHT-icCM9awcX4eS8feW0J_KIjsx2IxjcRRQOcQ
3 |
4 | site:
5 | title: Sponsor Test
6 |
7 | loadpoints:
8 | - title: Carport
9 | charger: easee_charger
10 |
11 | chargers:
12 | - name: easee_charger
13 | type: template
14 | template: easee
15 | user: test@example.org
16 | password: none
17 | charger: EH123456
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "include": ["env.d.ts", "assets/js/**/*", "tests/**/*"],
4 | "compilerOptions": {
5 | "allowUnreachableCode": false,
6 | "allowUnusedLabels": false,
7 | "noFallthroughCasesInSwitch": true,
8 | "noImplicitReturns": true,
9 | "noPropertyAccessFromIndexSignature": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "composite": true,
13 | "allowJs": true,
14 | "baseUrl": ".",
15 | "paths": {
16 | "@/*": ["assets/js/*"]
17 | },
18 | "plugins": [
19 | {
20 | "name": "typescript-eslint-language-service"
21 | }
22 | ]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/util/cloud/api.go:
--------------------------------------------------------------------------------
1 | package cloud
2 |
3 | import "errors"
4 |
5 | var (
6 | // ErrNotAuthorized indicates request token is not authorized
7 | ErrNotAuthorized = errors.New("not authorized")
8 |
9 | // ErrVehicleNotAvailable indicates vehicle not available, client should retry to prepare vehicle
10 | ErrVehicleNotAvailable = errors.New("vehicle not available")
11 | )
12 |
--------------------------------------------------------------------------------
/util/config/types.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | type Typed struct {
8 | Type string `json:"type"`
9 | Other map[string]any `mapstructure:",remain" yaml:",inline"` // TODO JSON serialization
10 | }
11 |
12 | type Named struct {
13 | Name string `json:"name"`
14 | Type string `json:"type"`
15 | Other map[string]any `mapstructure:",remain" yaml:",inline"` // TODO JSON serialization
16 | }
17 |
18 | // Property returns the value of the named property
19 | func (n Named) Property(key string) any {
20 | for k, v := range n.Other {
21 | if strings.EqualFold(k, key) {
22 | return v
23 | }
24 | }
25 | return nil
26 | }
27 |
--------------------------------------------------------------------------------
/util/duration.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "strconv"
5 | "time"
6 | )
7 |
8 | func ParseDuration(s string) (time.Duration, error) {
9 | v, err := strconv.Atoi(s)
10 | if err != nil {
11 | return 0, err
12 | }
13 |
14 | return time.Duration(v) * time.Second, nil
15 | }
16 |
--------------------------------------------------------------------------------
/util/encode/encode_test.go:
--------------------------------------------------------------------------------
1 | package encode
2 |
3 | import (
4 | "math"
5 | "testing"
6 | "time"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestEncode(t *testing.T) {
12 | enc := NewEncoder(WithDuration())
13 | assert.Equal(t, nil, enc.Encode(time.Time{}))
14 | assert.Equal(t, nil, enc.Encode(math.NaN()))
15 | assert.Equal(t, nil, enc.Encode(math.Inf(0)))
16 | assert.Equal(t, 30, enc.Encode(30*time.Second))
17 | assert.Equal(t, 3.142, enc.Encode(math.Pi))
18 | }
19 |
--------------------------------------------------------------------------------
/util/env.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "log"
5 | "os"
6 | "strings"
7 | )
8 |
9 | func Getenv(key string, def ...string) string {
10 | res := strings.TrimSpace(os.Getenv(key))
11 | if res == "" {
12 | if len(def) == 1 {
13 | return def[0]
14 | }
15 |
16 | log.Fatalln("missing", key)
17 | }
18 | return res
19 | }
20 |
--------------------------------------------------------------------------------
/util/format_functions.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "time"
4 |
5 | func timeRound(d time.Duration, round string) time.Duration {
6 | switch round {
7 | case "s", "sec":
8 | return d.Round(time.Second)
9 | case "m", "min":
10 | return d.Round(time.Minute)
11 | default:
12 | return d
13 | }
14 | }
15 |
16 | func addDate(ts time.Time, y, m, d int) time.Time {
17 | return ts.AddDate(y, m, d)
18 | }
19 |
--------------------------------------------------------------------------------
/util/locale/internal/types.go:
--------------------------------------------------------------------------------
1 | package internal
2 |
3 | // ContextKey is just an empty struct. It exists so context values can be
4 | // an immutable public variable with a unique type. It's immutable
5 | // because nobody else can create a ContextKey, being unexported.
6 | type ContextKey struct{}
7 |
--------------------------------------------------------------------------------
/util/locale/locale_test.go:
--------------------------------------------------------------------------------
1 | package locale
2 |
3 | import (
4 | "os"
5 | "testing"
6 |
7 | "github.com/evcc-io/evcc/server/assets"
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 | )
11 |
12 | func TestLocales(t *testing.T) {
13 | assets.I18n = os.DirFS("../../i18n")
14 | require.NoError(t, Init())
15 | assert.Less(t, 1, len(Bundle.LanguageTags()))
16 | }
17 |
--------------------------------------------------------------------------------
/util/log_context.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "context"
5 | )
6 |
7 | var CtxLogger = struct{}{}
8 |
9 | func WithLogger(ctx context.Context, log *Logger) context.Context {
10 | return context.WithValue(ctx, CtxLogger, log)
11 | }
12 |
--------------------------------------------------------------------------------
/util/log_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/evcc-io/evcc/util/logstash"
7 | jww "github.com/spf13/jwalterweatherman"
8 | "github.com/stretchr/testify/require"
9 | )
10 |
11 | func TestLogger(t *testing.T) {
12 | log := NewLogger("test")
13 | log.TRACE.Print("foo")
14 |
15 | require.Len(t, logstash.All(nil, jww.LevelTrace, 0), 1)
16 | }
17 |
--------------------------------------------------------------------------------
/util/logstash/element.go:
--------------------------------------------------------------------------------
1 | package logstash
2 |
3 | import (
4 | "regexp"
5 | "slices"
6 |
7 | jww "github.com/spf13/jwalterweatherman"
8 | )
9 |
10 | type element string
11 |
12 | var re = regexp.MustCompile(`^\[(.+?)\s*\] (\w+) `)
13 |
14 | func (e element) areaLevel() (string, jww.Threshold) {
15 | m := re.FindAllStringSubmatch(string(e), 1)
16 | if len(m) != 1 || len(m[0]) != 3 {
17 | return "", jww.LevelError
18 | }
19 | return m[0][1], LogLevelToThreshold(m[0][2])
20 | }
21 |
22 | func (e element) match(areas []string, level jww.Threshold) bool {
23 | a, l := e.areaLevel()
24 | return (len(areas) == 0 || slices.Contains(areas, a)) && l >= level
25 | }
26 |
--------------------------------------------------------------------------------
/util/logstash/levels.go:
--------------------------------------------------------------------------------
1 | package logstash
2 |
3 | import (
4 | "strings"
5 |
6 | jww "github.com/spf13/jwalterweatherman"
7 | )
8 |
9 | // LogLevelToThreshold converts log level string to a jww Threshold
10 | func LogLevelToThreshold(level string) jww.Threshold {
11 | switch strings.ToUpper(level) {
12 | case "FATAL":
13 | return jww.LevelFatal
14 | case "ERROR":
15 | return jww.LevelError
16 | case "WARN":
17 | return jww.LevelWarn
18 | case "INFO":
19 | return jww.LevelInfo
20 | case "DEBUG":
21 | return jww.LevelDebug
22 | case "TRACE":
23 | return jww.LevelTrace
24 | default:
25 | return jww.LevelError
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/util/metering.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | // SignFromPower is a helper function to create signed current from signed power bypassing already signed current
4 | func SignFromPower(current, power float64) float64 {
5 | if current > 0 && power < 0 {
6 | return -current
7 | }
8 | return current
9 | }
10 |
--------------------------------------------------------------------------------
/util/modbus/mutex.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import "sync"
4 |
5 | var mu2 sync.Mutex
6 |
7 | func Lock() {
8 | mu2.Lock()
9 | }
10 |
11 | func Unlock() {
12 | mu2.Unlock()
13 | }
14 |
--------------------------------------------------------------------------------
/util/param_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestParam(t *testing.T) {
10 | lp := 2
11 | p := Param{
12 | Key: "power",
13 | Val: 4711,
14 | }
15 | assert.Equal(t, "power", p.UniqueID())
16 |
17 | p.Loadpoint = &lp
18 | assert.Equal(t, "2.power", p.UniqueID())
19 | }
20 |
21 | func TestParamCache(t *testing.T) {
22 | NewParamCache().Add("foo", Param{})
23 | }
24 |
--------------------------------------------------------------------------------
/util/request/json.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "io"
7 | )
8 |
9 | // errorReader wraps an error with an io.Reader
10 | type errorReader struct {
11 | err error
12 | }
13 |
14 | func (r *errorReader) Read(p []byte) (int, error) {
15 | return 0, r.err
16 | }
17 |
18 | func (r *errorReader) Seek(offset int64, whence int) (int64, error) {
19 | return 0, r.err
20 | }
21 |
22 | // MarshalJSON marshals JSON into an io.ReadSeeker
23 | func MarshalJSON(data any) io.ReadSeeker {
24 | if data == nil {
25 | return nil
26 | }
27 |
28 | body, err := json.Marshal(data)
29 | if err != nil {
30 | return &errorReader{err: err}
31 | }
32 |
33 | return bytes.NewReader(body)
34 | }
35 |
--------------------------------------------------------------------------------
/util/request/xml.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | import (
4 | "bytes"
5 | "encoding/xml"
6 | "io"
7 | )
8 |
9 | // MarshalXML marshals XML into an io.ReadSeeker
10 | func MarshalXML(data any) io.ReadSeeker {
11 | if data == nil {
12 | return nil
13 | }
14 |
15 | body, err := xml.Marshal(data)
16 | if err != nil {
17 | return &errorReader{err: err}
18 | }
19 |
20 | return bytes.NewReader(body)
21 | }
22 |
--------------------------------------------------------------------------------
/util/telemetry/types.go:
--------------------------------------------------------------------------------
1 | package telemetry
2 |
3 | type InstanceChargeProgress struct {
4 | InstanceID string `json:"instanceId"`
5 | ChargeProgress
6 | }
7 |
8 | type ChargeProgress struct {
9 | ChargePower float64 `json:"chargePower"`
10 | GreenPower float64 `json:"greenPower"`
11 | ChargeEnergy float64 `json:"chargeEnergy"`
12 | GreenEnergy float64 `json:"greenEnergy"`
13 | }
14 |
--------------------------------------------------------------------------------
/util/templates/class.go:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | type Class int
4 |
5 | //go:generate go tool enumer -type Class -transform=lower
6 | const (
7 | _ Class = iota
8 | Charger
9 | Meter
10 | Vehicle
11 | Tariff
12 | Loadpoint
13 | Circuit
14 | )
15 |
--------------------------------------------------------------------------------
/util/templates/includes/eebus.tpl:
--------------------------------------------------------------------------------
1 | {{ define "eebus" }}
2 | type: eebus
3 | ski: {{ .ski }}
4 | {{ if .ip }}ip: {{ .ip }}{{ end }}
5 | {{- end}}
6 |
--------------------------------------------------------------------------------
/util/templates/includes/mqtt.tpl:
--------------------------------------------------------------------------------
1 | {{ define "mqtt" }}
2 | broker: {{ .host }}:{{ .port }}
3 | {{- if .user }}
4 | user: {{ .user }}
5 | {{- end }}
6 | {{- if .password }}
7 | password: {{ .password }}
8 | {{- end }}
9 | {{- if ne .timeout "30s" }}
10 | timeout: {{ .timeout }}
11 | {{- end }}
12 | {{- if .caCert }}
13 | caCert: {{ .caCert }}
14 | {{- end }}
15 | {{- if .clientCert }}
16 | clientCert: {{ .clientCert }}
17 | {{- end }}
18 | {{- if .clientKey }}
19 | clientKey: {{ .clientKey }}
20 | {{- end }}
21 | {{- end }}
22 |
--------------------------------------------------------------------------------
/util/templates/includes/switchsocket.tpl:
--------------------------------------------------------------------------------
1 | {{ define "switchsocket" }}
2 | standbypower: {{ .standbypower }}
3 | features:
4 | {{- if and .integrateddevice (ne .integrateddevice "false") }}
5 | - integrateddevice
6 | {{- end }}
7 | {{- if and .heating (ne .heating "false") }}
8 | - heating
9 | {{- end }}
10 | {{- if .icon }}
11 | icon: {{ .icon }}
12 | {{- end }}
13 | {{- end }}
14 |
--------------------------------------------------------------------------------
/util/templates/includes/tariff-base.tpl:
--------------------------------------------------------------------------------
1 | {{ define "tariff-base" }}
2 | {{- if .charges }}
3 | charges: {{ .charges }}
4 | {{- end }}
5 | {{- if .tax }}
6 | tax: {{ .tax }}
7 | {{- end }}
8 | {{- if .formula }}
9 | formula: {{ .formula }}
10 | {{- end }}
11 | {{- end }}
12 |
--------------------------------------------------------------------------------
/util/templates/includes/tariff-features.tpl:
--------------------------------------------------------------------------------
1 | {{ define "tariff-features" }}
2 | {{- if eq .average "true" }}
3 | features: ["average"]
4 | {{- end }}
5 | {{- end }}
6 |
--------------------------------------------------------------------------------
/util/templates/includes/vehicle-base.tpl:
--------------------------------------------------------------------------------
1 | {{ define "vehicle-base" }}
2 | user: {{ .user }}
3 | password: {{ .password }}
4 | vin: {{ .vin }}
5 | {{ template "vehicle-common" . }}
6 | {{- if .cache }}
7 | cache: {{ .cache }}
8 | {{- end }}
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/util/templates/includes/vehicle-features.tpl:
--------------------------------------------------------------------------------
1 | {{ define "vehicle-features" }}
2 | {{- if or (eq .coarsecurrent "true") (eq .welcomecharge "true") (eq .streaming "true") }}
3 | features:
4 | {{- if eq .coarsecurrent "true" }}
5 | - coarsecurrent
6 | {{- end }}
7 | {{- if eq .welcomecharge "true" }}
8 | - welcomecharge
9 | {{- end }}
10 | {{- if eq .streaming "true" }}
11 | - streaming
12 | {{- end }}
13 | {{- end }}
14 | {{- end }}
15 |
--------------------------------------------------------------------------------
/util/templates/includes/vehicle-language.tpl:
--------------------------------------------------------------------------------
1 | {{ define "vehicle-language" }}
2 | language: {{ .language }}
3 | {{- end }}
4 |
--------------------------------------------------------------------------------
/util/templates/merge_test.go:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | )
8 |
9 | func TestMergeMaps(t *testing.T) {
10 | target := map[string]any{
11 | "foo": "bar",
12 | "nested": map[string]any{
13 | "bar": "baz",
14 | },
15 | }
16 | other := map[string]any{
17 | "Foo": 1,
18 | "Nested": map[string]any{
19 | "Bar": 2,
20 | },
21 | "baz": 3,
22 | }
23 |
24 | require.NoError(t, mergeMaps(other, target))
25 | require.Equal(t, map[string]any{
26 | "foo": 1,
27 | "nested": map[string]any{
28 | "bar": 2,
29 | },
30 | "baz": 3,
31 | }, target)
32 | }
33 |
--------------------------------------------------------------------------------
/util/templates/paramtype.go:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | type ParamType int
4 |
5 | //go:generate go tool enumer -type ParamType -trimprefix Type -text
6 | const (
7 | TypeString ParamType = iota // default type string
8 | TypeBool
9 | TypeChoice
10 | TypeChargeModes
11 | TypeDuration
12 | TypeFloat
13 | TypeInt
14 | TypeList
15 | )
16 |
--------------------------------------------------------------------------------
/util/templates/proxy.tpl:
--------------------------------------------------------------------------------
1 | type: template
2 | template: {{ .Template }}
3 | {{- range .Params }}
4 | {{- if or (ne (len .Value) 0) (ne (len .Values) 0) }}
5 | {{ .Name }}:
6 | {{- if len .Value }} {{ .Value }} {{ end }}
7 | {{- if ne (len .Values) 0 }}
8 | {{- range .Values }}
9 | - {{ . }}
10 | {{- end }}
11 | {{- end }}
12 | {{- end -}}
13 | {{ end -}}
14 |
--------------------------------------------------------------------------------
/util/templates/usage.go:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | type Usage int
4 |
5 | //go:generate go tool enumer -type Usage -trimprefix Usage -transform=lower -text
6 | const (
7 | UsageGrid Usage = iota
8 | UsagePV
9 | UsageBattery
10 | UsageCharge
11 | UsageAux
12 | )
13 |
--------------------------------------------------------------------------------
/util/test/ci.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "os"
5 | "testing"
6 | )
7 |
8 | func SkipCI(t *testing.T) {
9 | t.Helper()
10 |
11 | if os.Getenv("CI") != "" {
12 | t.Skip("Skipping testing in CI environment")
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/util/test/errors.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | // Acceptable checks if a test error is in the list of acceptable errors
8 | func Acceptable(err error, acceptable []string) bool {
9 | for _, msg := range acceptable {
10 | err := strings.TrimSpace(err.Error())
11 | if strings.HasPrefix(err, msg) || strings.HasSuffix(err, msg) {
12 | return true
13 | }
14 | }
15 |
16 | return false
17 | }
18 |
--------------------------------------------------------------------------------
/util/token.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "time"
5 |
6 | "golang.org/x/oauth2"
7 | )
8 |
9 | func TokenWithExpiry(token *oauth2.Token) *oauth2.Token {
10 | if token != nil && token.Expiry.IsZero() && token.ExpiresIn != 0 {
11 | token.Expiry = time.Now().Add(time.Second * time.Duration(token.ExpiresIn))
12 | }
13 | return token
14 | }
15 |
--------------------------------------------------------------------------------
/util/transport/basicauth.go:
--------------------------------------------------------------------------------
1 | package transport
2 |
3 | import (
4 | "encoding/base64"
5 | "net/http"
6 | )
7 |
8 | // BasicAuthHeader returns the basic auth header
9 | func BasicAuthHeader(user, password string) string {
10 | return "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+password))
11 | }
12 |
13 | // BasicAuth creates an http transport performing basic auth
14 | func BasicAuth(user, password string, base http.RoundTripper) http.RoundTripper {
15 | return &Decorator{
16 | Decorator: DecorateHeaders(map[string]string{
17 | "Authorization": BasicAuthHeader(user, password),
18 | }),
19 | Base: base,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/util/transport/bearer.go:
--------------------------------------------------------------------------------
1 | package transport
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | // BearerAuth creates an HTTP transport performing HTTP authorization using an OAuth 2.0 Bearer Token
8 | func BearerAuth(token string, base http.RoundTripper) http.RoundTripper {
9 | return &Decorator{
10 | Decorator: DecorateHeaders(map[string]string{
11 | "Authorization": "Bearer " + token,
12 | }),
13 | Base: base,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/util/version.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "fmt"
4 |
5 | const DevVersion = "0.0.0"
6 |
7 | var (
8 | // Version of executable
9 | Version = DevVersion
10 |
11 | // Commit of executable
12 | Commit = ""
13 | )
14 |
15 | func FormattedVersion() string {
16 | if Commit != "" {
17 | return fmt.Sprintf("%s (%s)", Version, Commit)
18 | }
19 | return Version
20 | }
21 |
--------------------------------------------------------------------------------
/vehicle/audi/etron/params.go:
--------------------------------------------------------------------------------
1 | package etron
2 |
3 | import "net/url"
4 |
5 | // Authorization parameters
6 | var AuthParams = url.Values{
7 | "response_type": {"code id_token token"},
8 | "client_id": {"f4d0934f-32bf-4ce4-b3c4-699a7049ad26@apps_vw-dilab_com"},
9 | "redirect_uri": {"myaudi:///"},
10 | "scope": {"openid profile mbb"}, // vin badge birthdate nickname email address phone name picture
11 | "prompt": {"login"},
12 | "ui_locales": {"de-DE"},
13 | }
14 |
15 | var IDKParams = url.Values{
16 | "client_id": {"f4d0934f-32bf-4ce4-b3c4-699a7049ad26@apps_vw-dilab_com"},
17 | "redirect_uri": {"myaudi:///"},
18 | }
19 |
20 | const AZSConfig = "myaudi"
21 |
--------------------------------------------------------------------------------
/vehicle/audi/etron/types.go:
--------------------------------------------------------------------------------
1 | package etron
2 |
3 | type Vehicle struct {
4 | VIN, Type, Nickname string
5 | }
6 |
--------------------------------------------------------------------------------
/vehicle/bmw/connected/types.go:
--------------------------------------------------------------------------------
1 | package bmw
2 |
3 | type Vehicle struct {
4 | VIN string
5 | Model string
6 | AppVehicleType string
7 | }
8 |
9 | type VehicleStatus struct {
10 | StatusCode int
11 | Message string
12 | State struct {
13 | CurrentMileage int64
14 | Range int64
15 | ElectricChargingState struct {
16 | ChargingLevelPercent int64
17 | Range int64
18 | IsChargerConnected bool
19 | ChargingStatus string
20 | ChargingTarget int64
21 | }
22 | ClimateControlState struct {
23 | Activity string
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/vehicle/ford/autonomic/types.go:
--------------------------------------------------------------------------------
1 | package autonomic
2 |
3 | import "time"
4 |
5 | type IntValue struct {
6 | UpdateTime time.Time
7 | Value int
8 | }
9 | type FloatValue struct {
10 | UpdateTime time.Time
11 | Value float64
12 | }
13 | type StringValue struct {
14 | UpdateTime time.Time
15 | Value string
16 | }
17 |
18 | type MetricsResponse struct {
19 | Metrics struct {
20 | Position struct {
21 | Value struct {
22 | Location struct {
23 | Lat, Lon float64
24 | }
25 | }
26 | }
27 | Odometer FloatValue
28 | XevPlugChargerStatus StringValue
29 | XevBatteryRange FloatValue
30 | XevBatteryStateOfCharge FloatValue
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/vehicle/ford/types.go:
--------------------------------------------------------------------------------
1 | package ford
2 |
3 | type VehiclesResponse struct {
4 | UserVehicles struct {
5 | VehicleDetails []struct {
6 | VIN string
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/vehicle/psa/helper.go:
--------------------------------------------------------------------------------
1 | package psa
2 |
3 | import (
4 | "sync"
5 |
6 | "golang.org/x/oauth2"
7 | )
8 |
9 | var (
10 | mu sync.Mutex
11 | identities = make(map[string]oauth2.TokenSource)
12 | )
13 |
14 | func getInstance(subject string) oauth2.TokenSource {
15 | return identities[subject]
16 | }
17 |
18 | func addInstance(subject string, identity oauth2.TokenSource) {
19 | identities[subject] = identity
20 | }
21 |
--------------------------------------------------------------------------------
/vehicle/saic/requests/api_config.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | const (
4 | CONTENT_ENCRYPTED = "1"
5 | PARAM_AUTHENTICATION = "Basic c3dvcmQ6c3dvcmRfc2VjcmV0"
6 | TENANT_ID = "459771"
7 | USER_TYPE = "app"
8 | )
9 |
--------------------------------------------------------------------------------
/vehicle/saic/requests/hashUtils.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "crypto/md5"
5 | "crypto/sha1"
6 | "crypto/sha256"
7 | "encoding/hex"
8 | )
9 |
10 | func Md5(value string) string {
11 | if len(value) == 0 {
12 | return ""
13 | }
14 | result := md5.Sum([]byte(value))
15 | return hex.EncodeToString(result[:])
16 | }
17 |
18 | func Sha1(value string) string {
19 | if len(value) == 0 {
20 | return ""
21 | }
22 | result := sha1.Sum([]byte(value))
23 | return hex.EncodeToString(result[:])
24 | }
25 |
26 | func Sha256(value string) string {
27 | if len(value) == 0 {
28 | return ""
29 | }
30 | result := sha256.Sum256([]byte(value))
31 | return hex.EncodeToString(result[:])
32 | }
33 |
--------------------------------------------------------------------------------
/vehicle/saic/requests/macUtils.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "crypto/hmac"
5 | "crypto/sha256"
6 | "encoding/hex"
7 | )
8 |
9 | func HmacSha256(secret string, message string) string {
10 | if len(secret) == 0 || len(message) == 0 {
11 | return ""
12 | }
13 |
14 | key := []byte(secret)
15 | h := hmac.New(sha256.New, key)
16 | h.Write([]byte(message))
17 | return hex.EncodeToString(h.Sum(nil))
18 | }
19 |
--------------------------------------------------------------------------------
/vehicle/seat/cupra/params.go:
--------------------------------------------------------------------------------
1 | package cupra
2 |
3 | import (
4 | "golang.org/x/oauth2"
5 | )
6 |
7 | var OAuth2Config = &oauth2.Config{
8 | ClientID: "3c756d46-f1ba-4d78-9f9a-cff0d5292d51@apps_vw-dilab_com",
9 | ClientSecret: "eb8814e641c81a2640ad62eeccec11c98effc9bccd4269ab7af338b50a94b3a2",
10 | RedirectURL: "cupra://oauth-callback",
11 | Scopes: []string{"openid", "profile", "mbb"},
12 | }
13 |
--------------------------------------------------------------------------------
/vehicle/seat/params.go:
--------------------------------------------------------------------------------
1 | package seat
2 |
3 | import "net/url"
4 |
5 | const (
6 | Brand = "VW"
7 | Country = "ES"
8 |
9 | // Authorization ClientID
10 | AuthClientID = "9dcc70f0-8e79-423a-a3fa-4065d99088b4"
11 | )
12 |
13 | // Authorization parameters
14 | var AuthParams = url.Values{
15 | "response_type": {"code id_token"}, // token
16 | "client_id": {"3c8e98bc-3ae9-4277-a563-d5ee65ddebba@apps_vw-dilab_com"},
17 | "redirect_uri": {"seatconnect://identity-kit/login"},
18 | "scope": {"openid profile"}, // address phone email birthdate nationalIdentifier cars mbb dealers badge nationality
19 | }
20 |
--------------------------------------------------------------------------------
/vehicle/skoda/params.go:
--------------------------------------------------------------------------------
1 | package skoda
2 |
3 | import "net/url"
4 |
5 | const (
6 | Brand = "VW"
7 | Country = "CZ"
8 |
9 | // Authorization ClientID
10 | AuthClientID = "afb0473b-6d82-42b8-bfea-cead338c46ef"
11 | )
12 |
13 | // Skoda native api
14 | var AuthParams = url.Values{
15 | "response_type": {"code id_token"},
16 | "client_id": {"7f045eee-7003-4379-9968-9355ed2adb06@apps_vw-dilab_com"},
17 | "redirect_uri": {"myskoda://redirect/login/"},
18 | "scope": {"address badge birthdate cars driversLicense dealers email mileage mbb nationalIdentifier openid phone profession profile vin"},
19 | }
20 |
21 | // TokenRefreshService parameters
22 | var TRSParams = url.Values{
23 | "brand": {"skoda"},
24 | }
25 |
--------------------------------------------------------------------------------
/vehicle/smart/hello/const.go:
--------------------------------------------------------------------------------
1 | package hello
2 |
3 | const (
4 | ApiURI = "https://api.ecloudeu.com"
5 | ApiKey = "3_L94eyQ-wvJhWm7Afp1oBhfTGXZArUfSHHW9p9Pncg513hZELXsxCfMWHrF8f5P5a"
6 |
7 | appID = "SmartAPPEU"
8 | operatorCode = "SMART"
9 | userAgent = "Mozilla/5.0 (Linux; Android 9; ANE-LX1 Build/HUAWEIANE-L21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/118.0.0.0 Mobile Safari/537.36"
10 | )
11 |
--------------------------------------------------------------------------------
/vehicle/template.go:
--------------------------------------------------------------------------------
1 | package vehicle
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/evcc-io/evcc/api"
7 | "github.com/evcc-io/evcc/util/templates"
8 | )
9 |
10 | func init() {
11 | registry.AddCtx("template", NewVehicleFromTemplateConfig)
12 | }
13 |
14 | func NewVehicleFromTemplateConfig(ctx context.Context, other map[string]any) (api.Vehicle, error) {
15 | instance, err := templates.RenderInstance(templates.Vehicle, other)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | return NewFromConfig(ctx, instance.Type, instance.Other)
21 | }
22 |
--------------------------------------------------------------------------------
/vehicle/tesla/helper_test.go:
--------------------------------------------------------------------------------
1 | package tesla
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestApiError(t *testing.T) {
10 | assert.Nil(t, apiError(nil))
11 | }
12 |
--------------------------------------------------------------------------------
/vehicle/tesla/types.go:
--------------------------------------------------------------------------------
1 | package tesla
2 |
3 | import (
4 | tesla "github.com/evcc-io/tesla-proxy-client"
5 | )
6 |
7 | type (
8 | Vehicle = tesla.Vehicle
9 | VehicleData = tesla.VehicleData
10 | CommandResponse = tesla.CommandResponse
11 | )
12 |
13 | type RegionResponse struct {
14 | Response Region
15 | }
16 |
17 | type Region struct {
18 | Region string
19 | FleetApiBaseUrl string `json:"fleet_api_base_url"`
20 | }
21 |
--------------------------------------------------------------------------------
/vehicle/toyota/provider.go:
--------------------------------------------------------------------------------
1 | package toyota
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/evcc-io/evcc/util"
7 | )
8 |
9 | type Provider struct {
10 | status func() (Status, error)
11 | }
12 |
13 | func NewProvider(api *API, vin string, cache time.Duration) *Provider {
14 | impl := &Provider{
15 | status: util.Cached(func() (Status, error) {
16 | return api.Status(vin)
17 | }, cache),
18 | }
19 | return impl
20 | }
21 |
22 | func (v *Provider) Soc() (float64, error) {
23 | res, err := v.status()
24 | return float64(res.Payload.BatteryLevel), err
25 | }
26 |
--------------------------------------------------------------------------------
/vehicle/tronity/auth.go:
--------------------------------------------------------------------------------
1 | package tronity
2 |
3 | import (
4 | "golang.org/x/oauth2"
5 | )
6 |
7 | const URI = "https://api.tronity.tech"
8 |
9 | func OAuth2Config(id, secret string) (*oauth2.Config, error) {
10 | return &oauth2.Config{
11 | ClientID: id,
12 | ClientSecret: secret,
13 | Endpoint: oauth2.Endpoint{
14 | AuthURL: "https://auth.tronity.io/oauth/v2/authorize",
15 | TokenURL: "https://api.tronity.tech/authentication",
16 | },
17 | Scopes: []string{"read_vin", "read_vehicle_info", "read_odometer", "read_charge", "read_charge", "read_battery", "read_location", "write_charge_start_stop", "write_wake_up"},
18 | }, nil
19 | }
20 |
--------------------------------------------------------------------------------
/vehicle/vag/cariad/const.go:
--------------------------------------------------------------------------------
1 | package cariad
2 |
3 | const BaseURL = "https://emea.bff.cariad.digital"
4 |
--------------------------------------------------------------------------------
/vehicle/vag/challenge.go:
--------------------------------------------------------------------------------
1 | package vag
2 |
3 | import (
4 | "net/url"
5 |
6 | "golang.org/x/oauth2"
7 | )
8 |
9 | func ChallengeAndVerifier(q url.Values) func(url.Values) {
10 | cv := oauth2.GenerateVerifier()
11 |
12 | q.Set("code_challenge_method", "S256")
13 | q.Set("code_challenge", oauth2.S256ChallengeFromVerifier(cv))
14 |
15 | return func(q url.Values) {
16 | q.Set("code_verifier", cv)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/vehicle/vag/loginapps/token.go:
--------------------------------------------------------------------------------
1 | package loginapps
2 |
3 | import (
4 | "encoding/json"
5 | "time"
6 |
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | // Token is the loginapps token
11 | type Token oauth2.Token
12 |
13 | func (t *Token) UnmarshalJSON(data []byte) error {
14 | var s struct {
15 | AccessToken string
16 | RefreshToken string
17 | }
18 |
19 | err := json.Unmarshal(data, &s)
20 | if err == nil {
21 | t.TokenType = "bearer"
22 | t.AccessToken = s.AccessToken
23 | t.RefreshToken = s.RefreshToken
24 | t.Expiry = time.Now().Add(time.Hour)
25 | }
26 |
27 | return err
28 | }
29 |
--------------------------------------------------------------------------------
/vehicle/vag/loginapps/token_test.go:
--------------------------------------------------------------------------------
1 | package loginapps
2 |
3 | import (
4 | "encoding/json"
5 | "testing"
6 | )
7 |
8 | func TestUnmarshalJSON(t *testing.T) {
9 | var tok Token
10 | str := `{"accesstoken":"access","refreshtoken":"refresh"}`
11 |
12 | if err := json.Unmarshal([]byte(str), &tok); err != nil {
13 | t.Error(err)
14 | }
15 |
16 | if tok.AccessToken != "access" {
17 | t.Error("AccessToken")
18 | }
19 |
20 | if tok.RefreshToken != "refresh" {
21 | t.Error("RefreshToken")
22 | }
23 |
24 | if tok.TokenType != "bearer" {
25 | t.Error("TokenType")
26 | }
27 |
28 | if tok.Expiry.IsZero() {
29 | t.Error("Expiry")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/vehicle/vw/id/params.go:
--------------------------------------------------------------------------------
1 | package id
2 |
3 | import (
4 | "net/url"
5 |
6 | "github.com/evcc-io/evcc/vehicle/vag/cariad"
7 | )
8 |
9 | const LoginURL = cariad.BaseURL + "/user-login/v1/authorize"
10 |
11 | var AuthParams = url.Values{
12 | "response_type": {"code id_token token"},
13 | "client_id": {"a24fba63-34b3-4d43-b181-942111e6bda8@apps_vw-dilab_com"},
14 | "redirect_uri": {"weconnect://authenticated"},
15 | "scope": {"openid profile badge cars vin"}, // dealers
16 | }
17 |
--------------------------------------------------------------------------------
/vehicle/vw/params.go:
--------------------------------------------------------------------------------
1 | package vw
2 |
3 | import "net/url"
4 |
5 | const (
6 | Brand = "VW"
7 | Country = "DE"
8 |
9 | // Authorization ClientID
10 | AuthClientID = "38761134-34d0-41f3-9a73-c4be88d7d337"
11 | )
12 |
13 | // Authorization parameters
14 | var AuthParams = url.Values{
15 | "response_type": {"code id_token token"},
16 | "client_id": {"9496332b-ea03-4091-a224-8c746b885068@apps_vw-dilab_com"},
17 | "redirect_uri": {"carnet://identity-kit/login"},
18 | "scope": {"openid profile mbb"}, // cars birthdate nickname address phone
19 | }
20 |
21 | // TokenRefreshService parameters
22 | var TRSParams = url.Values{
23 | "brand": {"vw"},
24 | }
25 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { mergeConfig } from "vite";
2 | import { defineConfig } from "vitest/config";
3 | import viteConfig from "./vite.config";
4 |
5 | export default mergeConfig(
6 | viteConfig,
7 | defineConfig({
8 | test: {
9 | environment: "happy-dom",
10 | },
11 | })
12 | );
13 |
--------------------------------------------------------------------------------