- 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.
| 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 |
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
Default item=SenecGridVoltagePh3
Default item=SenecGridFrequency
Default item=SenecBatteryVoltage
- Default item=SenecLiveBatCharge
- Default item=SenecLiveBatDischarge
- Default item=SenecLiveGridImport
- Default item=SenecLiveGridExport
}
}
```
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;
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();
* 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();
*/
@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);
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";
* 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;
}
@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;
}
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);
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
@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
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);
+ }
+ }
}
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 + "]";
}
}
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
<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"/>
<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>
<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>
--- /dev/null
+<?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>