mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[evcc] Battery values are not shown anymore (#20309)
* [evcc] syntactic simplification (#20246) Signed-off-by: Maik Scheibler <maik@scheibler-family.de>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
# evcc Binding
|
||||
|
||||
This binding integrates [evcc](https://evcc.io), an extensible **E**lectric **V**ehicle **C**harge **C**ontroller and home energy management system.
|
||||
The binding is compatible to evcc [version 0.123.1](https://github.com/evcc-io/evcc/releases/tag/0.123.1) or newer and was tested with [version 0.205.0](https://github.com/evcc-io/evcc/releases/tag/0.205.0).
|
||||
The binding is compatible to evcc [version 0.209.8](https://github.com/evcc-io/evcc/releases/tag/0.209.8) or newer and was tested with [version 0.301.1](https://github.com/evcc-io/evcc/releases/tag/0.301.1).
|
||||
|
||||
You can easily install and upgrade evcc on openHABian using `sudo openhabian-config`.
|
||||
**Important compatibility note:** Starting with this version of the binding, evcc versions **below 0.209.8** are no longer supported due to changes in the evcc API.
|
||||
If you are currently running evcc `< 0.209.8`, you must either upgrade your evcc installation to at least `0.209.8` or continue using an older version of this binding that still supports the legacy API.
|
||||
|
||||
evcc controls your wallbox(es) with multiple charging modes and allows you to charge your ev with your photovoltaic's excess current.
|
||||
To provide an intelligent charging control, evcc supports over 30 wallboxes and over 20 energy meters/home energy management systems from many manufacturers as well as electric vehicles from over 20 car manufacturers.
|
||||
|
||||
+1
@@ -76,6 +76,7 @@ public class EvccBindingConstants {
|
||||
// JSON Keys
|
||||
public static final String JSON_KEY_ACTIVE = "active";
|
||||
public static final String JSON_KEY_BATTERY = "battery";
|
||||
public static final String JSON_KEY_DEVICES = "devices";
|
||||
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";
|
||||
|
||||
+11
-6
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.evcc.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
|
||||
import static org.openhab.binding.evcc.internal.EvccBindingConstants.SUPPORTED_THING_TYPES;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -37,7 +37,7 @@ import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* The {@link EvccDiscoveryService} is responsible for scanning the API response for things
|
||||
*
|
||||
*
|
||||
* @author Marcel Goerentz - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@@ -66,10 +66,15 @@ public class EvccDiscoveryService extends AbstractThingHandlerDiscoveryService<E
|
||||
JsonObject state = thingHandler.getCachedEvccState().deepCopy();
|
||||
if (!state.isEmpty()) {
|
||||
for (EvccDiscoveryMapper mapper : mappers) {
|
||||
mapper.discover(state, thingHandler).forEach(thing -> {
|
||||
logger.debug("Thing discovered {}", thing);
|
||||
thingDiscovered(thing);
|
||||
});
|
||||
try {
|
||||
mapper.discover(state, thingHandler).forEach(thing -> {
|
||||
logger.debug("Thing discovered {}", thing);
|
||||
thingDiscovered(thing);
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
// isolate the mapper calls so that a single evcc API change does not affect stable parts
|
||||
logger.warn("discovery for things failed, possibly due to an API change", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -16,7 +16,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.Normalizer;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@@ -32,7 +31,7 @@ public class Utils {
|
||||
|
||||
/**
|
||||
* This method removes specific local characters (like ä, ö, ü), so we get a sanitized string
|
||||
*
|
||||
*
|
||||
* @param name that will be sanitized
|
||||
* @return a sanitized name that has replaced any invalid char
|
||||
*/
|
||||
@@ -49,10 +48,10 @@ public class Utils {
|
||||
* This method creates a stable ID string based on the provided list of values (cut down to first 10 hex chars of
|
||||
* SHA-256)
|
||||
*
|
||||
* @param values list of strings to create the ID from
|
||||
* @param values strings to create the ID from
|
||||
* @return a stable ID string
|
||||
*/
|
||||
public static String createIdString(List<String> values) {
|
||||
public static String createIdString(String... values) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] digest = md.digest(String.join("", values).getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
+27
-21
@@ -14,10 +14,11 @@ package org.openhab.binding.evcc.internal.discovery.mapper;
|
||||
|
||||
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.evcc.internal.EvccBindingConstants;
|
||||
@@ -29,10 +30,11 @@ import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* The {@link BatteryDiscoveryMapper} iis responsible for mapping the discovered batteries to discovery results
|
||||
* The {@link BatteryDiscoveryMapper} is responsible for mapping the discovered batteries to discovery results.
|
||||
*
|
||||
* @author Marcel Goerentz - Initial contribution
|
||||
*/
|
||||
@@ -42,24 +44,28 @@ public class BatteryDiscoveryMapper implements EvccDiscoveryMapper {
|
||||
|
||||
@Override
|
||||
public Collection<DiscoveryResult> discover(JsonObject state, EvccBridgeHandler bridgeHandler) {
|
||||
List<DiscoveryResult> results = new ArrayList<>();
|
||||
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(JSON_KEY_TITLE)
|
||||
? battery.get(JSON_KEY_TITLE).getAsString().toLowerCase(Locale.ROOT)
|
||||
: JSON_KEY_BATTERY + i;
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
return getBatteryArray(state).stream()
|
||||
.flatMap(deviceArray -> StreamSupport.stream(deviceArray.spliterator(), false))
|
||||
.filter(JsonElement::isJsonObject).map(JsonElement::getAsJsonObject).map(battery -> {
|
||||
int index = counter.getAndIncrement();
|
||||
String title = battery.has(JSON_KEY_TITLE)
|
||||
? battery.get(JSON_KEY_TITLE).getAsString().toLowerCase(Locale.ROOT)
|
||||
: JSON_KEY_BATTERY + index;
|
||||
|
||||
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_TITLE, title).withRepresentationProperty(PROPERTY_TITLE).build();
|
||||
results.add(result);
|
||||
}
|
||||
return results;
|
||||
ThingUID uid = new ThingUID(EvccBindingConstants.THING_TYPE_BATTERY,
|
||||
bridgeHandler.getThing().getUID(), Utils.sanitizeName(title));
|
||||
return DiscoveryResultBuilder.create(uid).withLabel(title)
|
||||
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_INDEX, index)
|
||||
.withProperty(PROPERTY_TITLE, title).withRepresentationProperty(PROPERTY_TITLE).build();
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private Optional<JsonArray> getBatteryArray(JsonObject state) {
|
||||
return Optional.ofNullable(state.get(JSON_KEY_BATTERY)).map(battElement -> battElement.isJsonArray()
|
||||
// for up to version 0.300.0
|
||||
? (JsonArray) battElement
|
||||
// for version 0.300.0+
|
||||
: ((JsonObject) battElement).getAsJsonArray(JSON_KEY_DEVICES));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ public class ForecastDiscoveryMapper implements EvccDiscoveryMapper {
|
||||
ThingUID uid = new ThingUID(THING_TYPE_FORECAST, bridgeHandler.getThing().getUID(),
|
||||
Utils.sanitizeName(forecastType));
|
||||
String label = "Forecast " + capitalizeFirstLetter(forecastType);
|
||||
String id = Utils.createIdString(List.of(label));
|
||||
String id = Utils.createIdString(label);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(label)
|
||||
.withBridge(bridgeHandler.getThing().getUID()).withProperty(PROPERTY_TYPE, PROPERTY_FORECAST)
|
||||
.withProperty(PROPERTY_SUBTYPE, forecastType).withProperty(PROPERTY_ID, id)
|
||||
|
||||
+4
-4
@@ -83,16 +83,16 @@ public class PlanDiscoveryMapper implements EvccDiscoveryMapper {
|
||||
String localizedLabel = tp.getText(ctx.getBundle(), "discovery.evcc.plan.one-time.label",
|
||||
"One-time charging plan for {0}", lp.getLocale(), title);
|
||||
String label = localizedLabel == null ? "One-time charging plan for " + title : localizedLabel;
|
||||
results.add(createPlanDiscoveryResult(label, Utils.createIdString(List.of(id, "Plan", String.valueOf(0))),
|
||||
0, id, bridgeHandler));
|
||||
results.add(createPlanDiscoveryResult(label, Utils.createIdString(id, "Plan", String.valueOf(0)), 0, id,
|
||||
bridgeHandler));
|
||||
}
|
||||
if (vehicle.has(JSON_KEY_REPEATING_PLANS) && vehicle.get(JSON_KEY_REPEATING_PLANS).isJsonArray()) {
|
||||
for (int index = 1; index <= vehicle.get(JSON_KEY_REPEATING_PLANS).getAsJsonArray().size(); index++) {
|
||||
String localizedLabel = tp.getText(ctx.getBundle(), "discovery.evcc.plan.repeating.label",
|
||||
"Repeating plan {0} for {1}", lp.getLocale(), index, title);
|
||||
String label = localizedLabel == null ? "Repeating plan " + index + " for " + title : localizedLabel;
|
||||
results.add(createPlanDiscoveryResult(label,
|
||||
Utils.createIdString(List.of(id, "Plan", String.valueOf(index))), index, id, bridgeHandler));
|
||||
results.add(createPlanDiscoveryResult(label, Utils.createIdString(id, "Plan", String.valueOf(index)),
|
||||
index, id, bridgeHandler));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
||||
+12
-3
@@ -22,6 +22,8 @@ import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
@@ -50,20 +52,27 @@ public class EvccBatteryHandler extends EvccBaseThingHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject state = stateOpt.getAsJsonArray(JSON_KEY_BATTERY).get(index).getAsJsonObject();
|
||||
JsonObject state = getStateFromCachedState(stateOpt);
|
||||
commonInitialize(state);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareApiResponseForChannelStateUpdate(JsonObject state) {
|
||||
state = state.has(JSON_KEY_BATTERY) ? state.getAsJsonArray(JSON_KEY_BATTERY).get(index).getAsJsonObject()
|
||||
state = state.has(JSON_KEY_BATTERY) && state.getAsJsonObject(JSON_KEY_BATTERY).has(JSON_KEY_DEVICES)
|
||||
? getStateFromCachedState(state)
|
||||
: new JsonObject();
|
||||
updateStatesFromApiResponse(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getStateFromCachedState(JsonObject state) {
|
||||
return state.getAsJsonArray(JSON_KEY_BATTERY).get(index).getAsJsonObject();
|
||||
JsonElement battElement = state.get(JSON_KEY_BATTERY);
|
||||
JsonArray battArray = battElement.isJsonArray()
|
||||
// for up to version 0.300.0
|
||||
? (JsonArray) battElement
|
||||
// for version 0.300.0+
|
||||
: ((JsonObject) battElement).getAsJsonArray(JSON_KEY_DEVICES);
|
||||
return battArray.get(index).getAsJsonObject();
|
||||
}
|
||||
}
|
||||
|
||||
+9
-6
@@ -15,6 +15,8 @@ package org.openhab.binding.evcc.internal.handler;
|
||||
import static org.openhab.binding.evcc.internal.EvccBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -37,12 +39,13 @@ public class EvccHeatingHandler extends EvccLoadpointHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EvccHeatingHandler.class);
|
||||
|
||||
private static final Map<String, String> JSON_KEYS = Map.ofEntries(
|
||||
Map.entry("effectiveLimitTemperature", JSON_KEY_EFFECTIVE_LIMIT_SOC),
|
||||
Map.entry("effectivePlanTemperature", JSON_KEY_EFFECTIVE_PLAN_SOC),
|
||||
Map.entry("limitTemperature", JSON_KEY_LIMIT_SOC),
|
||||
Map.entry("vehicleLimitTemperature", JSON_KEY_VEHICLE_LIMIT_SOC),
|
||||
Map.entry("vehicleTemperature", JSON_KEY_VEHICLE_SOC));
|
||||
// SortedMap to have a stable replacement result, the unit test is relying on it
|
||||
private static final SortedMap<String, String> JSON_KEYS = new TreeMap<>(
|
||||
Map.ofEntries(Map.entry("effectiveLimitTemperature", JSON_KEY_EFFECTIVE_LIMIT_SOC),
|
||||
Map.entry("effectivePlanTemperature", JSON_KEY_EFFECTIVE_PLAN_SOC),
|
||||
Map.entry("limitTemperature", JSON_KEY_LIMIT_SOC),
|
||||
Map.entry("vehicleLimitTemperature", JSON_KEY_VEHICLE_LIMIT_SOC),
|
||||
Map.entry("vehicleTemperature", JSON_KEY_VEHICLE_SOC)));
|
||||
|
||||
public EvccHeatingHandler(Thing thing, ChannelTypeRegistry channelTypeRegistry) {
|
||||
super(thing, channelTypeRegistry);
|
||||
|
||||
-6
@@ -116,12 +116,6 @@ public class EvccSiteHandler extends EvccBaseThingHandler {
|
||||
state.add(JSON_KEY_GRID + Utils.capitalizeFirstLetter(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
// Backwards compatibility for evcc 0.209.8 and older
|
||||
JsonElement startup = state.get("startup");
|
||||
if (null != startup && startup.isJsonPrimitive() && startup.getAsJsonPrimitive().isBoolean()) {
|
||||
state.add("startupComplete", startup);
|
||||
state.remove("startup");
|
||||
}
|
||||
state.remove(JSON_KEY_GRID);
|
||||
state.remove(JSON_KEY_GRID_CONFIGURED);
|
||||
}
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.evcc.internal.handler;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -93,7 +93,7 @@ public class EvccBatteryHandlerTest extends AbstractThingHandlerTestClass<EvccBa
|
||||
EvccBridgeHandler bridgeHandler = mock(EvccBridgeHandler.class);
|
||||
handler.bridgeHandler = bridgeHandler;
|
||||
when(bridgeHandler.getCachedEvccState()).thenReturn(exampleResponse);
|
||||
batteryState = exampleResponse.getAsJsonArray("battery").get(0).getAsJsonObject();
|
||||
batteryState = exampleResponse.getAsJsonObject("battery").getAsJsonArray("devices").get(0).getAsJsonObject();
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
|
||||
+3
-2
@@ -101,11 +101,12 @@ public class EvccHeatingHandlerTest extends AbstractThingHandlerTestClass<EvccHe
|
||||
heatingObject.remove(JSON_KEY_LIMIT_SOC);
|
||||
heatingObject.remove(JSON_KEY_EFFECTIVE_PLAN_SOC);
|
||||
heatingObject.remove(JSON_KEY_VEHICLE_LIMIT_SOC);
|
||||
// prepare expectation with alphabetically sorted keys, the handler is doing the same ordering
|
||||
heatingObject.addProperty("effectiveLimitTemperature", 80);
|
||||
heatingObject.addProperty("vehicleTemperature", 48.3);
|
||||
heatingObject.addProperty("limitTemperature", 80);
|
||||
heatingObject.addProperty("effectivePlanTemperature", 20);
|
||||
heatingObject.addProperty("limitTemperature", 80);
|
||||
heatingObject.addProperty("vehicleLimitTemperature", 80);
|
||||
heatingObject.addProperty("vehicleTemperature", 48.3);
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
|
||||
+1
-5
@@ -14,9 +14,7 @@ package org.openhab.binding.evcc.internal.handler;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
@@ -98,10 +96,8 @@ public class EvccSiteHandlerTest extends AbstractThingHandlerTestClass<EvccSiteH
|
||||
modifiedVerifyObject.addProperty("gridVoltageL1", 230.0);
|
||||
modifiedVerifyObject.addProperty("gridVoltageL2", 231.0);
|
||||
modifiedVerifyObject.addProperty("gridVoltageL3", 229.0);
|
||||
modifiedVerifyObject.addProperty("startupComplete", verifyObject.get("startup").getAsBoolean());
|
||||
modifiedVerifyObject.remove("gridConfigured");
|
||||
modifiedVerifyObject.remove("grid");
|
||||
modifiedVerifyObject.remove("startup");
|
||||
|
||||
gridConfigured.addProperty("power", 2000);
|
||||
gridConfigured.addProperty("energy", 10000);
|
||||
|
||||
+2794
-1365
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user