added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.senechome-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-senechome" description="SenecHome Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.senechome/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
import java.util.Date;
/**
* The {@link PowerLimitationStatusDTO} class is used as a POJO to
* in-memory persist the last limitation state change.
*
* @author Steven Schwarznau - Initial contribution
*
*/
public class PowerLimitationStatusDTO {
public boolean state = false;
public long time = new Date().getTime();
}

View File

@@ -0,0 +1,151 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
/**
* The {@link SenecBatteryStatus} class defines available Senec specific
* battery states.
*
* @author Steven Schwarznau - Initial contribution
*
*/
public enum SenecBatteryStatus {
INITIALSTATE(0, "INITIAL STATE"),
ERROR_INVERTER_COMMUNICATION(1, "ERROR INVERTER COMMUNICATION"),
ERROR_ELECTRICY_METER(2, "ERROR ELECTRICY METER"),
RIPPLE_CONTROL_RECEIVER(3, "RIPPLE CONTROL RECEIVER"),
INITIAL_CHARGE(4, "INITIAL CHARGE"),
MAINTENANCE_CHARGE(5, "MAINTENANCE CHARGE"),
MAINTENANCE_READY(6, "MAINTENANCE READY"),
MAINTENANCE_REQUIRED(7, "MAINTENANCE REQUIRED"),
MAN_SAFETY_CHARGE(8, "MAN. SAFETY CHARGE"),
SAFETY_CHARGE_READY(9, "SAFETY CHARGE READY"),
FULL_CHARGE(10, "FULL CHARGE"),
EQUALIZATION_CHARGE(11, "EQUALIZATION: CHARGE"),
DESULFATATION_CHARGE(12, "DESULFATATION: CHARGE"),
BATTERY_FULL(13, "BATTERY FULL"),
CHARGE(14, "CHARGE"),
BATTERY_EMPTY(15, "BATTERY EMPTY"),
DISCHARGE(16, "DISCHARGE"),
PV_AND_DISCHARGE(17, "PV + DISCHARGE"),
GRID_AND_DISCHARGE(18, "GRID + DISCHARGE"),
PASSIVE(19, "PASSIVE"),
OFF(20, "OFF"),
OWN_CONSUMPTION(21, "OWN CONSUMPTION"),
RESTART(22, "RESTART"),
MAN_EQUALIZATION_CHARGE(23, "MAN. EQUALIZATION: CHARGE"),
MAN_DESULFATATION_CHARGE(24, "MAN. DESULFATATION: CHARGE"),
SAFETY_CHARGE(25, "SAFETY CHARGE"),
BATTERY_PROTECTION_MODE(26, "BATTERY PROTECTION MODE"),
EG_ERROR(27, "EG ERROR"),
EG_CHARGE(28, "EG CHARGE"),
EG_DISCHARGE(29, "EG DISCHARGE"),
EG_PASSIVE(30, "EG PASSIVE"),
EG_PROHIBIT_CHARGE(31, "EG PROHIBIT CHARGE"),
EG_PROHIBIT_DISCHARGE(32, "EG PROHIBIT DISCHARGE"),
EMERGANCY_CHARGE(33, "EMERGANCY CHARGE"),
SOFTWARE_UPDATE(34, "SOFTWARE UPDATE"),
NSP_ERROR(35, "NSP ERROR"),
NSP_ERROR_GRID(36, "NSP ERROR: GRID"),
NSP_ERROR_HARDWRE(37, "NSP ERROR: HARDWRE"),
NO_SERVER_CONNECTION(38, "NO SERVER CONNECTION"),
BMS_ERROR(39, "BMS ERROR"),
MAINTENANCE_FILTER(40, "MAINTENANCE: FILTER"),
SLEEPING_MODE(41, "SLEEPING MODE"),
WAITING_EXCESS(42, "WAITING EXCESS"),
CAPACITY_TEST_CHARGE(43, "CAPACITY TEST: CHARGE"),
CAPACITY_TEST_DISCHARGE(44, "CAPACITY TEST: DISCHARGE"),
MAN_DESULFATATION_WAIT(45, "MAN. DESULFATATION: WAIT"),
MAN_DESULFATATION_READY(46, "MAN. DESULFATATION: READY"),
MAN_DESULFATATION_ERROR(47, "MAN. DESULFATATION: ERROR"),
EQUALIZATION_WAIT(48, "EQUALIZATION: WAIT"),
EMERGANCY_CHARGE_ERROR(49, "EMERGANCY CHARGE: ERROR"),
MAN_EQUALIZATION_WAIT(50, "MAN. EQUALIZATION: WAIT"),
MAN_EQUALIZATION_ERROR(51, "MAN. EQUALIZATION: ERROR"),
MAN_EQUALIZATION_READY(52, "MAN: EQUALIZATION: READY"),
AUTO_DESULFATATION_WAIT(53, "AUTO. DESULFATATION: WAIT"),
ABSORPTION_PHASE(54, "ABSORPTION PHASE"),
DCSWITCH_OFF(55, "DC-SWITCH OFF"),
PEAKSHAVING_WAIT(56, "PEAK-SHAVING: WAIT"),
ERROR_BATTERY_INVERTER(57, "ERROR BATTERY INVERTER"),
NPUERROR(58, "NPU-ERROR"),
BMS_OFFLINE(59, "BMS OFFLINE"),
MAINTENANCE_CHARGE_ERROR(60, "MAINTENANCE CHARGE ERROR"),
MAN_SAFETY_CHARGE_ERROR(61, "MAN. SAFETY CHARGE ERROR"),
SAFETY_CHARGE_ERROR(62, "SAFETY CHARGE ERROR"),
NO_CONNECTION_TO_MASTER(63, "NO CONNECTION TO MASTER"),
LITHIUM_SAFE_MODE_ACTIVE(64, "LITHIUM SAFE MODE ACTIVE"),
LITHIUM_SAFE_MODE_DONE(65, "LITHIUM SAFE MODE DONE"),
BATTERY_VOLTAGE_ERROR(66, "BATTERY VOLTAGE ERROR"),
BMS_DC_SWITCHED_OFF(67, "BMS DC SWITCHED OFF"),
GRID_INITIALIZATION(68, "GRID INITIALIZATION"),
GRID_STABILIZATION(69, "GRID STABILIZATION"),
REMOTE_SHUTDOWN(70, "REMOTE SHUTDOWN"),
OFFPEAKCHARGE(71, "OFFPEAK-CHARGE"),
ERROR_HALFBRIDGE(72, "ERROR HALFBRIDGE"),
BMS_ERROR_OPERATING_TEMPERATURE(73, "BMS: ERROR OPERATING TEMPERATURE"),
FACOTRY_SETTINGS_NOT_FOUND(74, "FACOTRY SETTINGS NOT FOUND"),
BACKUP_POWER_MODE_ACTIVE(75, "BACKUP POWER MODE - ACTIVE"),
BACKUP_POWER_MODE_BATTERY_EMPTY(76, "BACKUP POWER MODE - BATTERY EMPTY"),
BACKUP_POWER_MODE_ERROR(77, "BACKUP POWER MODE ERROR"),
INITIALISING(78, "INITIALISING"),
INSTALLATION_MODE(79, "INSTALLATION MODE"),
GRID_OFFLINE(80, "GRID OFFLINE"),
BMS_UPDATE_NEEDED(81, "BMS UPDATE NEEDED"),
BMS_CONFIGURATION_NEEDED(82, "BMS CONFIGURATION NEEDED"),
INSULATION_TEST(83, "INSULATION TEST"),
SELFTEST(84, "SELFTEST"),
EXTERNAL_CONTROL(85, "EXTERNAL CONTROL"),
ERROR_TEMPERATURESENSOR(86, "ERROR: TEMPERATURESENSOR"),
GRID_OPERATOR_CHARGE_PROHIBITED(87, "GRID OPERATOR: CHARGE PROHIBITED"),
GRID_OPERATOR_DISCHARGE_PROHIBITED(88, "GRID OPERATOR: DISCHARGE PROHIBITED"),
SPARE_CAPACITY(89, "SPARE CAPACITY"),
SELFTEST_ERROR(90, "SELFTEST ERROR"),
EARTH_FAULT(91, "EARTH FAULT"),
UNKNOWN(-1, "UNKNOWN");
private int code;
private String description;
SenecBatteryStatus(int index, String description) {
this.code = index;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
public static SenecBatteryStatus fromCode(int code) {
for (SenecBatteryStatus state : SenecBatteryStatus.values()) {
if (state.code == code) {
return state;
}
}
return SenecBatteryStatus.UNKNOWN;
}
public static String descriptionFromCode(int code) {
for (SenecBatteryStatus state : SenecBatteryStatus.values()) {
if (state.code == code) {
return state.description;
}
}
return SenecBatteryStatus.UNKNOWN.description;
}
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.openhab.binding.senechome.internal.json.SenecHomeResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* The {@link SenecHomeApi} class configures http client and
* performs status requests
*
* @author Steven Schwarznau - Initial contribution
*
*/
@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();
private String hostname = "";
public SenecHomeApi(HttpClient httpClient) {
this.httpClient = httpClient;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* POST json with empty, but expected fields, to lala.cgi of Senec webinterface
* the response will contain the same fields, but with the corresponding values
*
* To receive new values, just modify the Json objects and add them to the thing channels
*
* @param hostname Hostname or ip address of senec battery
* @return Instance of SenecHomeResponse
* @throws MalformedURLException Configuration/URL is wrong
* @throws IOException Communication failed
*/
public SenecHomeResponse getStatistics()
throws InterruptedException, TimeoutException, ExecutionException, IOException {
String location = HTTP_PROTO_PREFIX + hostname;
Request request = httpClient.newRequest(location);
request.header(HttpHeader.ACCEPT, MimeTypes.Type.APPLICATION_JSON.asString());
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString());
ContentResponse response = request.method(HttpMethod.POST)
.content(new StringContentProvider(gson.toJson(new SenecHomeResponse()))).send();
if (response.getStatus() == HttpStatus.OK_200) {
return gson.fromJson(response.getContentAsString(), SenecHomeResponse.class);
} else {
logger.trace("Got unexpected response code {}", response.getStatus());
throw new IOException("Got unexpected response code " + response.getStatus());
}
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SenecHomeBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Steven Schwarznau - Initial contribution
*/
@NonNullByDefault
public class SenecHomeBindingConstants {
private 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_POWER_LIMITATION = "powerLimitation";
public static final String CHANNEL_SENEC_POWER_LIMITATION_STATE = "powerLimitationState";
public static final String CHANNEL_SENEC_BATTERY_STATE = "batteryState";
public static final String CHANNEL_SENEC_POWER_CONSUMPTION = "houseConsumption";
public static final String CHANNEL_SENEC_ENERGY_PRODUCTION = "energyProduction";
public static final String CHANNEL_SENEC_BATTERY_POWER = "batteryPower";
public static final String CHANNEL_SENEC_BATTERY_FUEL_CHARGE = "batteryFuelCharge";
public static final String CHANNEL_SENEC_GRID_POWER = "gridPower";
public static final String CHANNEL_SENEC_GRID_POWER_SUPPLY = "gridPowerSupply";
public static final String CHANNEL_SENEC_GRID_POWER_DRAW = "gridPowerDraw";
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
/**
* The {@link SenecHomeConfigurationDTO} class contains fields mapping thing configuration parameters.
*
* @author Steven Schwarznau - Initial contribution
*/
public class SenecHomeConfigurationDTO {
public String hostname;
public int refreshInterval = 15;
public int limitationTresholdValue = 95;
public int limitationDuration = 120;
}

View File

@@ -0,0 +1,255 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Power;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.senechome.internal.json.SenecHomeResponse;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SenecHomeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Steven Schwarznau - Initial contribution
*/
@NonNullByDefault
public class SenecHomeHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SenecHomeHandler.class);
private final static String VALUE_TYPE_INT = "u3";
private final static String VALUE_TYPE_UNSIGNED_INT = "u8";
private final static String VALUE_TYPE_FLOAT = "fl";
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable PowerLimitationStatusDTO limitationStatus = null;
private final @Nullable SenecHomeApi senecHomeApi;
private SenecHomeConfigurationDTO config = new SenecHomeConfigurationDTO();
private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState);
public SenecHomeHandler(Thing thing, HttpClient httpClient) {
super(thing);
this.senecHomeApi = new SenecHomeApi(httpClient);
}
@Override
public void handleRemoval() {
stopJobIfRunning();
super.handleRemoval();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command == REFRESH) {
logger.debug("Refreshing {}", channelUID);
refresh();
} else {
logger.trace("The SenecHome-Binding is a read-only binding and can not handle commands");
}
}
@Override
public void dispose() {
stopJobIfRunning();
super.dispose();
}
protected void stopJobIfRunning() {
final ScheduledFuture<?> refreshJob = this.refreshJob;
if (refreshJob != null && !refreshJob.isCancelled()) {
refreshJob.cancel(true);
this.refreshJob = null;
}
}
@Override
public void initialize() {
config = getConfigAs(SenecHomeConfigurationDTO.class);
senecHomeApi.setHostname(config.hostname);
refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refreshInterval, TimeUnit.SECONDS);
limitationStatus = null;
}
private void refresh() {
refreshCache.getValue();
}
public @Nullable Boolean refreshState() {
try {
SenecHomeResponse response = senecHomeApi.getStatistics();
logger.trace("received {}", response);
BigDecimal pvLimitation = new BigDecimal(100).subtract(getSenecValue(response.limitation.powerLimitation))
.setScale(0, RoundingMode.HALF_UP);
updateState(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_LIMITATION,
new QuantityType<Dimensionless>(pvLimitation, SmartHomeUnits.PERCENT));
Channel channelLimitationState = getThing()
.getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_LIMITATION_STATE);
updatePowerLimitationStatus(channelLimitationState,
(100 - pvLimitation.intValue()) <= config.limitationTresholdValue, config.limitationDuration);
Channel channelConsumption = getThing()
.getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_CONSUMPTION);
updateState(channelConsumption.getUID(),
new QuantityType<Power>(
getSenecValue(response.energy.homePowerConsumption).setScale(2, RoundingMode.HALF_UP),
SmartHomeUnits.WATT));
Channel channelEnergyProduction = getThing()
.getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_ENERGY_PRODUCTION);
updateState(channelEnergyProduction.getUID(),
new QuantityType<Power>(
getSenecValue(response.energy.inverterPowerGeneration).setScale(0, RoundingMode.HALF_UP),
SmartHomeUnits.WATT));
Channel channelBatteryPower = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_POWER);
updateState(channelBatteryPower.getUID(),
new QuantityType<Power>(
getSenecValue(response.energy.batteryPower).setScale(2, RoundingMode.HALF_UP),
SmartHomeUnits.WATT));
Channel channelBatteryFuelCharge = getThing()
.getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_FUEL_CHARGE);
updateState(channelBatteryFuelCharge.getUID(),
new QuantityType<Dimensionless>(
getSenecValue(response.energy.batteryFuelCharge).setScale(0, RoundingMode.HALF_UP),
SmartHomeUnits.PERCENT));
Channel channelBatteryState = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_STATE);
updateBatteryState(channelBatteryState, getSenecValue(response.energy.batteryState).intValue());
updateGridPowerValues(getSenecValue(response.grid.currentGridValue));
updateStatus(ThingStatus.ONLINE);
} catch (IOException | InterruptedException | TimeoutException | ExecutionException e) {
logger.warn("Error refreshing source '{}'", getThing().getUID(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Could not connect to Senec web interface:" + e.getMessage());
}
return Boolean.TRUE;
}
protected BigDecimal getSenecValue(String value) {
String[] type = value.split("_");
if (VALUE_TYPE_INT.equalsIgnoreCase(type[0])) {
return new BigDecimal(Integer.valueOf(type[1], 16));
} else if (VALUE_TYPE_UNSIGNED_INT.equalsIgnoreCase(type[0])) {
return new BigDecimal(Integer.valueOf(type[1], 16));
} else if (VALUE_TYPE_FLOAT.equalsIgnoreCase(type[0])) {
return parseFloatValue(type[1]);
} else {
logger.warn("Unknown value type [{}]", type[0]);
}
return BigDecimal.ZERO;
}
/**
* Parse the hex coded float value of Senec device and return as BigDecimal
*
* @param value String with hex float
* @return BigDecimal with float value
*/
private static BigDecimal parseFloatValue(String value) {
// sample: value = 43E26188
float f = Float.intBitsToFloat(Integer.parseUnsignedInt(value, 16));
return new BigDecimal(f);
}
protected void updatePowerLimitationStatus(Channel channel, boolean status, int duration) {
if (this.limitationStatus != null) {
if (this.limitationStatus.state == status) {
long stateSince = new Date().getTime() - this.limitationStatus.time;
if (((int) (stateSince / 1000)) < duration) {
// skip updating state (possible flapping state)
return;
} else {
logger.debug("{} longer than required duration {}", status, duration);
}
} else {
this.limitationStatus.state = status;
this.limitationStatus.time = new Date().getTime();
// skip updating state (state changed, possible flapping state)
return;
}
} else {
this.limitationStatus = new PowerLimitationStatusDTO();
this.limitationStatus.state = status;
}
logger.debug("Updating power limitation state {}", status);
updateState(channel.getUID(), status ? OnOffType.ON : OnOffType.OFF);
}
protected void updateBatteryState(Channel channel, int code) {
updateState(channel.getUID(), new StringType(SenecBatteryStatus.descriptionFromCode(code)));
}
protected void updateGridPowerValues(BigDecimal gridTotalValue) {
BigDecimal gridTotal = gridTotalValue.setScale(2, RoundingMode.HALF_UP);
Channel channelGridPower = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER);
if (channelGridPower != null) {
updateState(channelGridPower.getUID(), new QuantityType<Power>(gridTotal, SmartHomeUnits.WATT));
}
Channel channelGridPowerSupply = getThing()
.getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_SUPPLY);
if (channelGridPowerSupply != null) {
BigDecimal gridSupply = gridTotal.compareTo(BigDecimal.ZERO) < 0 ? gridTotal.abs() : BigDecimal.ZERO;
updateState(channelGridPowerSupply.getUID(), new QuantityType<Power>(gridSupply, SmartHomeUnits.WATT));
}
Channel channelGridPowerDraw = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_DRAW);
if (channelGridPowerDraw != null) {
BigDecimal gridDraw = gridTotal.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : gridTotal.abs();
updateState(channelGridPowerDraw.getUID(), new QuantityType<Power>(gridDraw, SmartHomeUnits.WATT));
}
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
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.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link SenecHomeHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Steven Schwarznau - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.senechome", service = ThingHandlerFactory.class)
public class SenecHomeHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.singleton(SenecHomeBindingConstants.THING_TYPE_SENEC_HOME_BATTERY);
private HttpClient httpClient;
@Activate
public SenecHomeHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SenecHomeBindingConstants.THING_TYPE_SENEC_HOME_BATTERY.equals(thingTypeUID)) {
return new SenecHomeHandler(thing, httpClient);
}
return null;
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
/**
* Json model of senec home devices: This sub model contains values of current workload, i. e. current consumption and
* battery charge.
*
* @author Steven Schwarznau - Initial Contribution
*/
public class SenecHomeEnergy implements Serializable {
private static final long serialVersionUID = -6171687327416551070L;
public @SerializedName("GUI_HOUSE_POW") String homePowerConsumption;
public @SerializedName("GUI_INVERTER_POWER") String inverterPowerGeneration;
public @SerializedName("GUI_BAT_DATA_POWER") String batteryPower;
public @SerializedName("GUI_BAT_DATA_FUEL_CHARGE") String batteryFuelCharge;
public @SerializedName("STAT_STATE") String batteryState;
@Override
public String toString() {
return "SenecHomeEnergy [homePowerConsumption=" + homePowerConsumption + ", inverterPowerGeneration="
+ inverterPowerGeneration + ", batteryPower=" + batteryPower + ", batteryFuelCharge="
+ batteryFuelCharge + ", batteryState=" + batteryState + "]";
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
/**
* Json model of senec home devices: This sub model provides the current power limitation by the inverter.
*
* @author Steven Schwarznau - Initial Contribution
*/
public class SenecHomeGrid implements Serializable {
private static final long serialVersionUID = -7479338321370375451L;
/**
* grid value indicating the current power draw (for values larger zero) or supply (for negative values)
*/
public @SerializedName("P_TOTAL") String currentGridValue;
@Override
public String toString() {
return "SenecHomeGrid [currentGridValue=" + currentGridValue + "]";
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
/**
* Json model of senec home devices: This sub model contains grid related power values.
*
* @author Steven Schwarznau - Initial Contribution
*/
public class SenecHomeLimitation implements Serializable {
private static final long serialVersionUID = -8990871346958824085L;
public @SerializedName("POWER_RATIO") String powerLimitation;
@Override
public String toString() {
return "SenecHomePower [powerLimitation=" + powerLimitation + "]";
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
/**
* Json model of senec home devices rebuilt by analyzing the api.
*
* @author Steven Schwarznau - Initial Contribution
*/
public class SenecHomeResponse implements Serializable {
private static final long serialVersionUID = 5302080655053778494L;
public @SerializedName("PV1") SenecHomeLimitation limitation = new SenecHomeLimitation();
public @SerializedName("ENERGY") SenecHomeEnergy energy = new SenecHomeEnergy();
public @SerializedName("PM1OBJ1") SenecHomeGrid grid = new SenecHomeGrid();
@Override
public String toString() {
return "SenecHomeResponse [limitation=" + limitation + ", energy=" + energy + ", grid=" + grid + "]";
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="senechome" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>SenecHome Binding</name>
<description>This is the binding for SenecHome.</description>
<author>Steven Schwarznau and Korbinian Probst</author>
</binding:binding>

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="senechome"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Sample Thing Type -->
<thing-type id="senechome">
<label>Senec Home</label>
<description>Senec Home</description>
<channels>
<channel id="powerLimitation" typeId="powerLimitation"/>
<channel id="powerLimitationState" typeId="powerLimitationState"/>
<channel id="houseConsumption" typeId="houseConsumption"/>
<channel id="energyProduction" typeId="energyProduction"/>
<channel id="batteryPower" typeId="batteryPower"/>
<channel id="batteryFuelCharge" typeId="batteryFuelCharge"/>
<channel id="batteryState" typeId="system.battery-level"/>
<channel id="gridPower" typeId="gridPower"/>
<channel id="gridPowerSupply" typeId="gridPowerSupply"/>
<channel id="gridPowerDraw" typeId="gridPowerDraw"/>
</channels>
<config-description>
<parameter name="hostname" type="text" required="true">
<label>Hostname/IP Address</label>
<description>Enter the hostname or ip address of your Senec Home device</description>
<context>network-address</context>
</parameter>
<parameter name="refreshInterval" type="integer" min="1" unit="s">
<label>Refresh Interval</label>
<description>Rate of refreshing details (in s)</description>
<default>15</default>
</parameter>
<parameter name="limitationTresholdValue" type="integer" min="0" max="100" unit="%">
<label>Limitation Treshold</label>
<description>Treshold in percent, defines when limitation state is enabled</description>
<default>95</default>
</parameter>
<parameter name="limitationDuration" type="integer" unit="s" min="1" max="1200">
<label>Limitation Duration</label>
<description>Duration of stable values until state is changed, defined in seconds</description>
<default>120</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="powerLimitation">
<item-type>Number:Dimensionless</item-type>
<label>Power Limitation</label>
<category>Energy</category>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="powerLimitationState">
<item-type>Switch</item-type>
<label>Limitation State</label>
<category>Energy</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="houseConsumption">
<item-type>Number:Power</item-type>
<label>Home Power Consumption</label>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="batteryPower">
<item-type>Number:Power</item-type>
<label>Battery Power</label>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="batteryFuelCharge">
<item-type>Number:Dimensionless</item-type>
<label>Battery Fuel</label>
<category>Battery</category>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="energyProduction">
<item-type>Number:Power</item-type>
<label>Solar Production</label>
<category>Energy</category>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="gridPower">
<item-type>Number:Power</item-type>
<label>Grid Power</label>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="gridPowerSupply">
<item-type>Number:Power</item-type>
<label>Grid Supply</label>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="gridPowerDraw">
<item-type>Number:Power</item-type>
<label>Grid Draw</label>
<category>Energy</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
</thing:thing-descriptions>