added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user