Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b6a3b5e
Add channels for grid currents, grid energy and charging currents.
marcelGoerentz Aug 23, 2025
7cda048
Correct IDs
marcelGoerentz Aug 23, 2025
c06cb34
First unit tests
marcelGoerentz Aug 26, 2025
39a5fab
Update EvccBaseThingHandlerTest.java so no file gets created
marcelGoerentz Aug 26, 2025
ead0de6
Fix SAT warnings
marcelGoerentz Aug 27, 2025
bfe2c83
Fix copilot findings
marcelGoerentz Aug 27, 2025
cad2f94
Add more unit tests
marcelGoerentz Aug 27, 2025
36140fe
Add unit tests for EvccBaseThingHandler, structure the unit tests, ad…
marcelGoerentz Aug 28, 2025
cabaa54
Fix review findings
marcelGoerentz Aug 31, 2025
fd12057
Remove utility method, fix typo in statistics.xml
marcelGoerentz Sep 2, 2025
526b942
Add more tests
marcelGoerentz Sep 3, 2025
a9cb2ed
Add tests for Heating, Loadpoint, PV and Site handler
marcelGoerentz Sep 4, 2025
8a0e552
Update tests, add example response and add a channel to loadpoints
marcelGoerentz Sep 10, 2025
cd89e4c
Update bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/thi…
marcelGoerentz Oct 14, 2025
9c3a25c
Update bundles/org.openhab.binding.evcc/src/main/resources/OH-INF/thi…
marcelGoerentz Oct 14, 2025
99cc29e
Add StateResolve class, fix unmutuable list issue
marcelGoerentz Oct 24, 2025
e1a82cc
Fix channels are not getting updated correctly
marcelGoerentz Oct 26, 2025
6e8fb0b
Update documentation for StateResolver
marcelGoerentz Oct 26, 2025
e17ca7e
Fix violations
marcelGoerentz Oct 26, 2025
8a258d3
Fix issue where phases configured API endpoint was not used correctly…
marcelGoerentz Oct 31, 2025
10b258a
Remove warnings from tests
marcelGoerentz Oct 31, 2025
4e11abb
Fix findings from lsiepels review
marcelGoerentz Nov 1, 2025
5d7483c
Fix spotless violations
marcelGoerentz Nov 1, 2025
958016a
Fix the low issues also
marcelGoerentz Nov 1, 2025
701b164
Fix markvis issues
marcelGoerentz Nov 1, 2025
3591814
Add markvis suggestions
marcelGoerentz Nov 2, 2025
4b5e20e
Fix issue where configuration index was not parsed correctly
marcelGoerentz Nov 2, 2025
c544150
Fix EvccBatteryHandlerTest
marcelGoerentz Nov 2, 2025
ea67653
Add limitenergy as case to the state resolver
marcelGoerentz Nov 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions bundles/org.openhab.binding.evcc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,25 @@ These channels are dynamically added to the Thing during their initialization; t
### `demo.things` Example

```java
Bridge evcc:server:demo-server "Demo" [scheme="https", url="demo.evcc.io", port=80, refreshInterval=30] {
Bridge evcc:server:demo-server "Demo" [scheme="https", host="demo.evcc.io", port=443, refreshInterval=30] {
// This thing will only exist once per evcc instance
Thing site demo-site "Site - evcc Demo"
Thing site
demo-site "Site - evcc Demo"
// You can define as many Battery things as you have batteries configured in your evcc instance
Thing battery demo-battery1 "Battery - evcc Demo Battery 1"
Thing battery
demo-battery1 "Battery - evcc Demo Battery 1"[index=0]
..
// You can define as many PV things as you have photovoltaics configured in your evcc instance
Thing pv demo-pv1 "PV - evcc Demo Photovoltaic 1"
Thing pv
demo-pv1 "PV - evcc Demo Photovoltaic 1"[index=0]
..
// You can define as many Loadpoint things as you have loadpoints configured in your evcc instance
Thing loadpoint demo-loadpoint-carport "Loadpoint - evcc Demo Loadpoint 1"
Thing loadpoint
demo-loadpoint-carport "Loadpoint - evcc Demo Loadpoint 1"[index=0]
..
// You can define as many Vehicle things as you have vehicles configured in your evcc instance
Thing vehicle demo-vehicle1 "Vehicle - evcc Demo Vehicle 1"
Thing vehicle
demo-vehicle1 "Vehicle - evcc Demo Vehicle 1"[id="vehicle_1"]
..
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,31 @@ public class EvccBindingConstants {
public static final String PROPERTY_TYPE_STATISTICS = "statistics";
public static final String PROPERTY_TYPE_VEHICLE = "vehicle";

public static final String JSON_MEMBER_BATTERY = "battery";
public static final String JSON_MEMBER_LOADPOINTS = "loadpoints";
public static final String JSON_MEMBER_PV = "pv";
public static final String JSON_MEMBER_STATISTICS = "statistics";
public static final String JSON_MEMBER_VEHICLES = "vehicles";
public static final String JSON_MEMBER_CHARGER_FEATURE_HEATING = "chargerFeatureHeating";
public static final String JSON_KEY_BATTERY = "battery";
public static final String JSON_KEY_CHARGE_CURRENT = "chargeCurrent";
public static final String JSON_KEY_CHARGE_CURRENTS = "chargeCurrents";
public static final String JSON_KEY_CHARGE_VOLTAGES = "chargeVoltages";
public static final String JSON_KEY_CHARGER_FEATURE_HEATING = "chargerFeatureHeating";
public static final String JSON_KEY_CHARGING = "charging";
public static final String JSON_KEY_CONNECTED = "connected";
public static final String JSON_KEY_EFFECTIVE_LIMIT_SOC = "effectiveLimitSoc";
public static final String JSON_KEY_EFFECTIVE_PLAN_SOC = "effectivePlanSoc";
public static final String JSON_KEY_ENABLED = "enabled";
public static final String JSON_KEY_GRID = "grid";
public static final String JSON_KEY_GRID_CONFIGURED = "gridConfigured";
public static final String JSON_KEY_LIMIT_SOC = "limitSoc";
public static final String JSON_KEY_LOADPOINTS = "loadpoints";
public static final String JSON_KEY_OFFERED_CURRENT = "offeredCurrent";
public static final String JSON_KEY_PHASES = "phases";
public static final String JSON_KEY_PHASES_CONFIGURED = "phasesConfigured";
public static final String JSON_KEY_PV = "pv";
public static final String JSON_KEY_SMART_COST_TYPE = "smartCostType";
public static final String JSON_KEY_STATISTICS = "statistics";
public static final String JSON_KEY_TITLE = "title";
public static final String JSON_KEY_VEHICLE_LIMIT_SOC = "vehicleLimitSoc";
public static final String JSON_KEY_VEHICLE_PRESENT = "vehiclePresent";
public static final String JSON_KEY_VEHICLE_SOC = "vehicleSoc";
public static final String JSON_KEY_VEHICLES = "vehicles";

public static final String NUMBER_CURRENCY = CoreItemFactory.NUMBER + ":Currency";
public static final String NUMBER_DIMENSIONLESS = CoreItemFactory.NUMBER + ":Dimensionless";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link EvccConfiguration} class contains fields mapping thing configuration parameters.
* The {@link EvccBridgeConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Florian Hotze - Initial contribution
* @author Marcel Goerentz - Rework the binding
*/
@NonNullByDefault
public class EvccConfiguration {
public class EvccBridgeConfiguration {
public String host = "";
public int pollInterval = 30;
public int port = 7070;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public EvccDiscoveryService() {
@Override
protected void startScan() {
logger.debug("Starting evcc Discover");
JsonObject state = thingHandler.getCachedEvccState();
JsonObject state = thingHandler.getCachedEvccState().deepCopy();
if (!state.isEmpty()) {
for (EvccDiscoveryMapper mapper : mappers) {
mapper.discover(state, thingHandler).forEach(thing -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ public class BatteryDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray batteries = state.getAsJsonArray(JSON_MEMBER_BATTERY);
JsonArray batteries = state.getAsJsonArray(JSON_KEY_BATTERY);
if (batteries == null) {
return results;
}
for (int i = 0; i < batteries.size(); i++) {
JsonObject battery = batteries.get(i).getAsJsonObject();
String title = battery.has("title") ? battery.get("title").getAsString().toLowerCase(Locale.ROOT)
: "battery" + i;
String title = battery.has(JSON_KEY_TITLE)
? battery.get(JSON_KEY_TITLE).getAsString().toLowerCase(Locale.ROOT)
: JSON_KEY_BATTERY + i;

ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_BATTERY, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_INDEX, i)
.withProperty(PROPERTY_TYPE, PROPERTY_TYPE_BATTERY).withProperty(PROPERTY_TITLE, title)
.withRepresentationProperty(PROPERTY_TITLE).build();
.withProperty(PROPERTY_TITLE, title).withRepresentationProperty(PROPERTY_TITLE).build();
results.add(result);
}
return results;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,26 @@ public class LoadpointDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray loadpoints = state.getAsJsonArray(JSON_MEMBER_LOADPOINTS);
JsonArray loadpoints = state.getAsJsonArray(JSON_KEY_LOADPOINTS);
if (loadpoints == null) {
return results;
}
for (int i = 0; i < loadpoints.size(); i++) {
JsonObject lp = loadpoints.get(i).getAsJsonObject();
String title = lp.has("title") ? lp.get("title").getAsString().toLowerCase(Locale.ROOT) : "loadpoint" + i;
String title = lp.has(JSON_KEY_TITLE) ? lp.get(JSON_KEY_TITLE).getAsString().toLowerCase(Locale.ROOT)
: "loadpoint" + i;

ThingUID uid = new ThingUID("DUMMY:DUMMY:DUMMY");
Map<String, Object> properties = new HashMap<>();
properties.put(PROPERTY_INDEX, i);
properties.put(PROPERTY_TITLE, title);

if (lp.has(JSON_MEMBER_CHARGER_FEATURE_HEATING)
&& lp.get(JSON_MEMBER_CHARGER_FEATURE_HEATING).getAsBoolean()) {
if (lp.has(JSON_KEY_CHARGER_FEATURE_HEATING) && lp.get(JSON_KEY_CHARGER_FEATURE_HEATING).getAsBoolean()) {
uid = new ThingUID(EvccBindingConstants.THING_TYPE_HEATING, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
properties.put(PROPERTY_TYPE, PROPERTY_TYPE_HEATING);
} else {
uid = new ThingUID(EvccBindingConstants.THING_TYPE_LOADPOINT, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
properties.put(PROPERTY_TYPE, PROPERTY_TYPE_LOADPOINT);
}

DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ public class PvDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonArray pvs = state.getAsJsonArray(JSON_MEMBER_PV);
JsonArray pvs = state.getAsJsonArray(JSON_KEY_PV);
if (pvs == null) {
return results;
}
for (int i = 0; i < pvs.size(); i++) {
JsonObject pv = pvs.get(i).getAsJsonObject();
String title = pv.has("title") ? pv.get("title").getAsString().toLowerCase(Locale.ROOT) : "pv" + i;
String title = pv.has(JSON_KEY_TITLE) ? pv.get(JSON_KEY_TITLE).getAsString().toLowerCase(Locale.ROOT)
: JSON_KEY_PV + i;

ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_PV, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_INDEX, i)
.withProperty(PROPERTY_TYPE, PROPERTY_TYPE_PV).withProperty(PROPERTY_TITLE, title)
.withRepresentationProperty(PROPERTY_TITLE).build();
.withProperty(PROPERTY_TITLE, title).withRepresentationProperty(PROPERTY_TITLE).build();

results.add(result);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler
String siteTitle = state.get("siteTitle").getAsString();
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_SITE, bridgeHandler.getThing().getUID(), "site");
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(siteTitle)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_SITE)
.withProperty(PROPERTY_SITE_TITLE, siteTitle).withRepresentationProperty(PROPERTY_SITE_TITLE).build();
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_SITE_TITLE, siteTitle)
.withRepresentationProperty(PROPERTY_SITE_TITLE).build();
results.add(result);
return results;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ public class StatisticsDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonObject statistics = state.getAsJsonObject(JSON_MEMBER_STATISTICS);
JsonObject statistics = state.getAsJsonObject(JSON_KEY_STATISTICS);
if (statistics == null) {
return results;
}
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_STATISTICS, bridgeHandler.getThing().getUID(),
"statistics");
JSON_KEY_STATISTICS);
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel("Statistics")
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_STATISTICS)
.withRepresentationProperty(PROPERTY_TYPE).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
*/
package org.openhab.binding.evcc.internal.discovery.mapper;

import static org.openhab.binding.evcc.internal.EvccBindingConstants.JSON_MEMBER_VEHICLES;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_ID;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_TYPE;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.PROPERTY_TYPE_VEHICLE;
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;

import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -44,20 +41,20 @@ public class VehicleDiscoveryMapper implements EvccDiscoveryMapper {
@Override
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
List<DiscoveryResult> results = new ArrayList<>();
JsonObject vehicles = state.getAsJsonObject(JSON_MEMBER_VEHICLES);
JsonObject vehicles = state.getAsJsonObject(JSON_KEY_VEHICLES);
if (vehicles == null) {
return results;
}
for (Map.Entry<String, JsonElement> entry : vehicles.entrySet()) {
JsonObject v = entry.getValue().getAsJsonObject();
String id = entry.getKey();
String title = v.has("title") ? v.get("title").getAsString() : id;
String title = v.has(JSON_KEY_TITLE) ? v.get(JSON_KEY_TITLE).getAsString() : id;

ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_VEHICLE, bridgeHandler.getThing().getUID(),
Utils.sanitizeName(title));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(title)
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_TYPE_VEHICLE)
.withProperty(PROPERTY_ID, id).withRepresentationProperty(PROPERTY_ID).build();
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_ID, id)
.withRepresentationProperty(PROPERTY_ID).build();

results.add(result);
}
Expand Down
Loading