[Senec] Fix for Senec firmware update (#15535)

* Fix for Senec update

Signed-off-by: querdenker2k <querdenker2k@gmx.de>
This commit is contained in:
Robert D 2023-11-11 14:01:37 +01:00 committed by GitHub
parent 6176b080c4
commit 1eacf67f34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 84 additions and 68 deletions

View File

@ -18,13 +18,14 @@ Examples: Lights, pool filters, wash machines, ...
- only V3, V3duo have a power generator and thus MPPs (V3 has 2 MPP, V3duo has 3 MPP)
- not equipped battery packs will return 0 for all ...Pack channels
- currently channels for the first wallbox are implemented (senec could handle 4 wallboxes)
- Senec disables http access at ~30.08.2023
## Thing Configuration
demo.things
```java
Thing senechome:senechome:pvbattery [ hostname="192.168.0.128", refreshInterval=60, limitationTresholdValue=70, limitationDuration=60 ]
Thing senechome:senechome:pvbattery [ hostname="192.168.0.128", refreshInterval=60, limitationTresholdValue=70, limitationDuration=60, useHttp=false ]
```
If the thing goes online then the connection to the web interface is successful.
@ -69,13 +70,6 @@ The property `limitationTresholdValue` is used as threshold for channel `powerLi
| gridVoltagePhase2 | volt | Grid voltage on Phase 2 |
| gridVoltagePhase3 | volt | Grid voltage on Phase 3 |
| gridFrequency | hertz | Grid frequency |
| liveBatCharge | kilo watt hour | Live Total Bat Charge |
| liveBatDischarge | kilo watt hour | Live Total Bat Discharge |
| liveGridImport | kilo watt hour | Live Total Grid Import |
| liveGridExport | kilo watt hour | Live Total Grid Export |
| liveHouseConsumption | kilo watt hour | Live Total House Consumption (without WB) |
| livePowerGenerator | kilo watt hour | Live Total PV generator generated energy |
| liveEnergyWallbox1 | kilo watt hour | Live Total Wallbox 1 charged energy |
| chargedEnergyPack1 | kilo watt hour | total charged energy battery pack 1 |
| chargedEnergyPack2 | kilo watt hour | total charged energy battery pack 2 |
| chargedEnergyPack3 | kilo watt hour | total charged energy battery pack 3 |
@ -141,10 +135,6 @@ Number SenecGridVoltagePh2 "Voltage Level on Phase 2 [%d V]" <e
Number SenecGridVoltagePh3 "Voltage Level on Phase 3 [%d V]" <energy> { channel="senechome:senechome:pvbattery:gridVoltagePhase3" }
Number SenecGridFrequency "Grid Frequency [%.2f Hz]" <energy> { channel="senechome:senechome:pvbattery:gridFrequency" }
Number SenecBatteryVoltage "Battery Voltage [%.1f V]" <energy> { channel="senechome:senechome:pvbattery:batteryVoltage" }
Number SenecLiveBatCharge "Live Bat Charge [%d kWh]" <energy> { channel="senechome:senechome:pvbattery:liveBatCharge" }
Number SenecLiveBatDischarge "Live Bat Discharge [%d kWh]" <energy> { channel="senechome:senechome:pvbattery:liveBatDischarge" }
Number SenecLiveGridImport "Live Grid Import [%d kWh]" <energy> { channel="senechome:senechome:pvbattery:liveGridImport" }
Number SenecLiveGridExport "Live Grid Export [%d kWh]" <energy> { channel="senechome:senechome:pvbattery:liveGridExport" }
```
## Sitemap
@ -176,10 +166,6 @@ Text label="Power Grid"{
Default item=SenecGridVoltagePh3
Default item=SenecGridFrequency
Default item=SenecBatteryVoltage
Default item=SenecLiveBatCharge
Default item=SenecLiveBatDischarge
Default item=SenecLiveGridImport
Default item=SenecLiveGridExport
}
}
```

View File

@ -13,7 +13,6 @@
package org.openhab.binding.senechome.internal;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -33,19 +32,17 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.MalformedJsonException;
/**
* The {@link SenecHomeApi} class configures http client and
* performs status requests
*
* @author Steven Schwarznau - Initial contribution
* @author Robert Delbrück - Update for Senec API changes
*
*/
@NonNullByDefault
public class SenecHomeApi {
private static final String HTTP_PROTO_PREFIX = "http://";
private final Logger logger = LoggerFactory.getLogger(SenecHomeApi.class);
private final HttpClient httpClient;
private final Gson gson = new Gson();
@ -66,28 +63,33 @@ public class SenecHomeApi {
* To receive new values, just modify the Json objects and add them to the thing channels
*
* @return Instance of SenecHomeResponse
* @throws MalformedURLException Configuration/URL is wrong
* @throws TimeoutException Communication failed (Timeout)
* @throws ExecutionException Communication failed
* @throws IOException Communication failed
* @throws InterruptedException Communication failed (Interrupted)
* @throws JsonSyntaxException Received response has an invalid json syntax
*/
public SenecHomeResponse getStatistics()
throws InterruptedException, TimeoutException, ExecutionException, IOException {
String location = HTTP_PROTO_PREFIX + hostname;
throws TimeoutException, ExecutionException, IOException, InterruptedException, JsonSyntaxException {
String location = hostname + "/lala.cgi";
logger.trace("sending request to: {}", location);
Request request = httpClient.newRequest(location);
request.header(HttpHeader.ACCEPT, MimeTypes.Type.APPLICATION_JSON.asString());
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString());
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString());
ContentResponse response = null;
try {
response = request.method(HttpMethod.POST)
.content(new StringContentProvider(gson.toJson(new SenecHomeResponse()))).send();
String dataToSend = gson.toJson(new SenecHomeResponse());
logger.trace("data to send: {}", dataToSend);
response = request.method(HttpMethod.POST).content(new StringContentProvider(dataToSend)).send();
if (response.getStatus() == HttpStatus.OK_200) {
return Objects.requireNonNull(gson.fromJson(response.getContentAsString(), SenecHomeResponse.class));
String responseString = response.getContentAsString();
return Objects.requireNonNull(gson.fromJson(responseString, SenecHomeResponse.class));
} else {
logger.trace("Got unexpected response code {}", response.getStatus());
throw new IOException("Got unexpected response code " + response.getStatus());
}
} catch (MalformedJsonException | JsonSyntaxException | InterruptedException | TimeoutException
| ExecutionException e) {
} catch (JsonSyntaxException | InterruptedException | TimeoutException | ExecutionException e) {
String errorMessage = "\nlocation: " + location;
errorMessage += "\nrequest: " + request.toString();
errorMessage += "\nrequest.getHeaders: " + request.getHeaders();

View File

@ -23,7 +23,7 @@ import org.openhab.core.thing.ThingTypeUID;
*/
@NonNullByDefault
public class SenecHomeBindingConstants {
private static final String BINDING_ID = "senechome";
protected static final String BINDING_ID = "senechome";
private static final String THING_BASE_ID = "senechome";
public static final ThingTypeUID THING_TYPE_SENEC_HOME_BATTERY = new ThingTypeUID(BINDING_ID, THING_BASE_ID);
@ -65,15 +65,6 @@ public class SenecHomeBindingConstants {
public static final String CHANNEL_SENEC_GRID_VOLTAGE_PH3 = "gridVoltagePhase3";
public static final String CHANNEL_SENEC_GRID_FREQUENCY = "gridFrequency";
// SenecHomeStatistics
public static final String CHANNEL_SENEC_LIVE_BAT_CHARGE = "liveBatCharge";
public static final String CHANNEL_SENEC_LIVE_BAT_DISCHARGE = "liveBatDischarge";
public static final String CHANNEL_SENEC_LIVE_GRID_IMPORT = "liveGridImport";
public static final String CHANNEL_SENEC_LIVE_GRID_EXPORT = "liveGridExport";
public static final String CHANNEL_SENEC_LIVE_HOUSE_CONSUMPTION = "liveHouseConsumption";
public static final String CHANNEL_SENEC_LIVE_POWER_GENERATOR = "livePowerGenerator";
public static final String CHANNEL_SENEC_LIVE_ENERGY_WALLBOX1 = "liveEnergyWallbox1";
// SenecHomeBattery
public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK1 = "chargedEnergyPack1";
public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK2 = "chargedEnergyPack2";

View File

@ -16,10 +16,12 @@ package org.openhab.binding.senechome.internal;
* The {@link SenecHomeConfigurationDTO} class contains fields mapping thing configuration parameters.
*
* @author Steven Schwarznau - Initial contribution
* @author Robert Delbrück - Add useHttp
*/
public class SenecHomeConfigurationDTO {
public String hostname;
public int refreshInterval = 15;
public int limitationTresholdValue = 95;
public int limitationDuration = 120;
public boolean useHttp = false;
}

View File

@ -124,7 +124,7 @@ public class SenecHomeHandler extends BaseThingHandler {
@Override
public void initialize() {
config = getConfigAs(SenecHomeConfigurationDTO.class);
senecHomeApi.setHostname(config.hostname);
senecHomeApi.setHostname("%s://%s".formatted(config.useHttp ? "http" : "https", config.hostname));
refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refreshInterval, TimeUnit.SECONDS);
limitationStatus = null;
}
@ -197,20 +197,6 @@ public class SenecHomeHandler extends BaseThingHandler {
updateQtyState(CHANNEL_SENEC_GRID_VOLTAGE_PH3, response.grid.currentGridVoltagePerPhase[2], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_GRID_FREQUENCY, response.grid.currentGridFrequency, 2, Units.HERTZ);
updateQtyState(CHANNEL_SENEC_LIVE_BAT_CHARGE, response.statistics.liveBatCharge, 2, Units.KILOWATT_HOUR);
updateQtyState(CHANNEL_SENEC_LIVE_BAT_DISCHARGE, response.statistics.liveBatDischarge, 2,
Units.KILOWATT_HOUR);
updateQtyState(CHANNEL_SENEC_LIVE_GRID_IMPORT, response.statistics.liveGridImport, 2, Units.KILOWATT_HOUR);
updateQtyState(CHANNEL_SENEC_LIVE_GRID_EXPORT, response.statistics.liveGridExport, 2, Units.KILOWATT_HOUR);
updateQtyState(CHANNEL_SENEC_LIVE_HOUSE_CONSUMPTION, response.statistics.liveHouseConsumption, 2,
Units.KILOWATT_HOUR);
updateQtyState(CHANNEL_SENEC_LIVE_POWER_GENERATOR, response.statistics.livePowerGenerator, 2,
Units.KILOWATT_HOUR);
if (response.statistics.liveWallboxEnergy != null) {
updateQtyState(CHANNEL_SENEC_LIVE_ENERGY_WALLBOX1, response.statistics.liveWallboxEnergy[0], 2,
Units.KILOWATT_HOUR, DIVISOR_ISO_TO_KILO);
}
if (response.battery.chargedEnergy != null) {
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK1, response.battery.chargedEnergy[0], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);

View File

@ -17,15 +17,19 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SenecHomeHandlerFactory} is responsible for creating things and thing
@ -36,15 +40,17 @@ import org.osgi.service.component.annotations.Reference;
@NonNullByDefault
@Component(configurationPid = "binding.senechome", service = ThingHandlerFactory.class)
public class SenecHomeHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(SenecHomeHandlerFactory.class);
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
.of(SenecHomeBindingConstants.THING_TYPE_SENEC_HOME_BATTERY);
private HttpClient httpClient;
private final HttpClient httpClient;
@Activate
public SenecHomeHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(true); // Accept all certificates
this.httpClient = httpClientFactory.createHttpClient(SenecHomeBindingConstants.BINDING_ID, sslContextFactory);
}
@Override
@ -62,4 +68,26 @@ public class SenecHomeHandlerFactory extends BaseThingHandlerFactory {
return null;
}
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
try {
httpClient.start();
} catch (Exception e) {
logger.warn("cannot start Jetty-Http-Client", e);
}
}
@Override
protected void deactivate(ComponentContext componentContext) {
super.deactivate(componentContext);
try {
httpClient.stop();
} catch (Exception e) {
logger.warn("cannot stop Jetty-Http-Client", e);
}
}
}

View File

@ -28,14 +28,13 @@ public class SenecHomeResponse implements Serializable {
public @SerializedName("PV1") SenecHomePower power = new SenecHomePower();
public @SerializedName("ENERGY") SenecHomeEnergy energy = new SenecHomeEnergy();
public @SerializedName("PM1OBJ1") SenecHomeGrid grid = new SenecHomeGrid();
public @SerializedName("STATISTIC") SenecHomeStatistics statistics = new SenecHomeStatistics();
public @SerializedName("BMS") SenecHomeBattery battery = new SenecHomeBattery();
public @SerializedName("TEMPMEASURE") SenecHomeTemperature temperature = new SenecHomeTemperature();
public @SerializedName("WALLBOX") SenecHomeWallbox wallbox = new SenecHomeWallbox();
@Override
public String toString() {
return "SenecHomeResponse [power=" + power + ", energy=" + energy + ", grid=" + grid + ", statistics="
+ statistics + "battery" + battery + "temperature" + temperature + "wallbox" + wallbox + "]";
return "SenecHomeResponse [power=" + power + ", energy=" + energy + ", grid=" + grid + ", battery" + battery
+ "temperature" + temperature + "wallbox" + wallbox + "]";
}
}

View File

@ -18,6 +18,8 @@ thing-type.config.senechome.senechome.limitationTresholdValue.label = Limitation
thing-type.config.senechome.senechome.limitationTresholdValue.description = Treshold in percent, defines when limitation state is enabled
thing-type.config.senechome.senechome.refreshInterval.label = Refresh Interval
thing-type.config.senechome.senechome.refreshInterval.description = Rate of refreshing details (in s)
thing-type.config.senechome.senechome.useHttp.label = Use HTTP
thing-type.config.senechome.senechome.useHttp.description = Use legacy http access instead of https
# channel types

View File

@ -48,15 +48,6 @@
<channel id="gridVoltagePhase3" typeId="gridVoltagePhase3"/>
<channel id="gridFrequency" typeId="gridFrequency"/>
<!-- SenecHomeStatistics -->
<channel id="liveBatCharge" typeId="liveBatCharge"/>
<channel id="liveBatDischarge" typeId="liveBatDischarge"/>
<channel id="liveGridImport" typeId="liveGridImport"/>
<channel id="liveGridExport" typeId="liveGridExport"/>
<channel id="liveHouseConsumption" typeId="liveHouseConsumption"/>
<channel id="livePowerGenerator" typeId="livePowerGenerator"/>
<channel id="liveEnergyWallbox1" typeId="liveEnergyWallbox1"/>
<!-- SenecHomeBattery -->
<channel id="chargedEnergyPack1" typeId="chargedEnergyPack1"/>
<channel id="chargedEnergyPack2" typeId="chargedEnergyPack2"/>
@ -101,6 +92,10 @@
<channel id="wallbox1ChargingPower" typeId="wallbox1ChargingPower"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
</properties>
<config-description>
<parameter name="hostname" type="text" required="true">
<label>Hostname/IP Address</label>
@ -122,6 +117,11 @@
<description>Duration of stable values until state is changed, defined in seconds</description>
<default>120</default>
</parameter>
<parameter name="useHttp" type="boolean" required="false">
<label>Use HTTP</label>
<description>Use legacy http access instead of https</description>
<default>false</default>
</parameter>
</config-description>
</thing-type>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="senechome:senechome">
<instruction-set targetVersion="1">
<remove-channel id="liveBatCharge"/>
<remove-channel id="liveBatDischarge"/>
<remove-channel id="liveGridImport"/>
<remove-channel id="liveGridExport"/>
<remove-channel id="liveHouseConsumption"/>
<remove-channel id="livePowerGenerator"/>
<remove-channel id="liveEnergyWallbox1"/>
</instruction-set>
</thing-type>
</update:update-descriptions>