initial implementation for Elero devices

This commit is contained in:
Av3m 2022-01-04 12:23:48 +01:00 committed by Thomas Vogl
parent e0bfb4e978
commit c34a9fd911
17 changed files with 844 additions and 148 deletions

View File

@ -24,11 +24,15 @@ import org.openhab.core.thing.ThingTypeUID;
@NonNullByDefault @NonNullByDefault
public class MediolaAioGatewayBindingConstants { public class MediolaAioGatewayBindingConstants {
private static final String BINDING_ID = "mediolaaiogateway"; public static final String BINDING_ID = "mediolaaiogateway";
// List of all Thing Type UIDs // List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample"); public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID THING_TYPE_ELERO_DEVICE = new ThingTypeUID(BINDING_ID, "elero-device");
// List of all Channel ids // List of all Channel ids
public static final String CHANNEL_1 = "channel1"; public static final String CHANNEL_CONTROL = "control";
public static final String CHANNEL_STATE = "state";
public static final String CHANNEL_CONTROL_BLIND = "control-blind";
public static final String CHANNEL_CONTROL_SWITCH = "control-switch";
} }

View File

@ -1,100 +0,0 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal;
import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link MediolaAioGatewayHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class MediolaAioGatewayHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(MediolaAioGatewayHandler.class);
private @Nullable MediolaAioGatewayConfiguration config;
public MediolaAioGatewayHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (CHANNEL_1.equals(channelUID.getId())) {
if (command instanceof RefreshType) {
// TODO: handle data refresh
}
// TODO: handle command
// Note: if communication with thing fails for some reason,
// indicate that by setting the status with detail information:
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
// "Could not control device at IP address x.x.x.x");
}
}
@Override
public void initialize() {
config = getConfigAs(MediolaAioGatewayConfiguration.class);
// TODO: Initialize the handler.
// The framework requires you to return from this method quickly. Also, before leaving this method a thing
// status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in
// case you can decide it directly.
// In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN
// access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the
// background.
// set the thing status to UNKNOWN temporarily and let the background task decide for the real status.
// the framework is then able to reuse the resources from the thing handler initialization.
// we set this upfront to reliably check status updates in unit tests.
updateStatus(ThingStatus.UNKNOWN);
// Example for background initialization:
scheduler.execute(() -> {
boolean thingReachable = true; // <background task with long running initialization here>
// when done do:
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
});
// These logging types should be primarily used by bindings
// logger.trace("Example trace message");
// logger.debug("Example debug message");
// logger.warn("Example warn message");
// Note: When initialization can NOT be done set the status with more details for further
// analysis. See also class ThingStatusDetail for all available status details.
// Add a description to give user information to understand why thing does not work as expected. E.g.
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
// "Can not access device as username and/or password are invalid");
}
}

View File

@ -10,19 +10,20 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.mediolaaiogateway.internal; package org.openhab.binding.mediolaaiogateway.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link MediolaAioGatewayConfiguration} class contains fields mapping thing configuration parameters. * The {@link MediolaAioBridgeConfig} class contains fields mapping thing configuration parameters.
* *
* @author Av3m - Initial contribution * @author Av3m - Initial contribution
*/ */
public class MediolaAioGatewayConfiguration { public class MediolaAioBridgeConfig {
/** /**
* Sample configuration parameters. Replace with your own. * Sample configuration parameters. Replace with your own.
*/ */
public String hostname; public String gatewayAddress;
public String password; public Integer gatewayPort;
public int refreshInterval;
} }

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.config;
/**
* The {@link MediolaAioEleroDeviceConfig} class contains fields mapping thing configuration parameters.
*
* @author Av3m - Initial contribution
*/
public class MediolaAioEleroDeviceConfig {
public Integer deviceAddress;
public Integer deviceSid;
public Integer refreshTime;
}

View File

@ -0,0 +1,124 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.discovery;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommandError;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommunicationError;
import org.openhab.binding.mediolaaiogateway.internal.handler.MediolaAioGatewayBridgeHandler;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioCommand;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioType;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.THING_TYPE_ELERO_DEVICE;
/**
* The {@link MediolaAioDeviceDiscoveryService} is used to discover devices that are connected to a Homematic gateway.
*
* @author Gerhard Riegler - Initial contribution
*/
public class MediolaAioDeviceDiscoveryService extends AbstractDiscoveryService
implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(MediolaAioDeviceDiscoveryService.class);
private static final int DISCOVER_TIMEOUT_SECONDS = 300;
private @NonNullByDefault({})
MediolaAioGatewayBridgeHandler bridgeHandler;
public MediolaAioDeviceDiscoveryService() {
super(Set.of(THING_TYPE_ELERO_DEVICE), DISCOVER_TIMEOUT_SECONDS, false);
logger.info("MediolaAioDeviceDiscoveryService constructor");
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
logger.info("MediolaAioDeviceDiscoveryService set thing handler");
if (handler instanceof MediolaAioGatewayBridgeHandler) {
this.bridgeHandler = (MediolaAioGatewayBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
super.activate(null);
}
@Override
public void deactivate() {
super.deactivate();
}
@Override
protected void startScan() {
Map<String, String> params = new HashMap<>();
params.put("config", "1");
try {
JsonElement devs = bridgeHandler.sendCommand(AioCommand.GetStates, params);
devs.getAsJsonArray().forEach((e) -> {
JsonObject o = e.getAsJsonObject();
if ( o.has("type") && o.get("type").getAsString().equals(AioType.Elero)) {
int address = Integer.parseInt(o.get("adr").getAsString(),16);
int sid = Integer.parseInt(o.get("sid").getAsString(),16);
String strAddress = String.format("%02X", address);
ThingUID thingUID = new ThingUID(THING_TYPE_ELERO_DEVICE, bridgeHandler.getThing().getUID(), strAddress);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withBridge(bridgeHandler.getThing().getUID())
.withLabel(String.format("Elero Device %s", strAddress))
.withProperty("deviceAddress", address)
.withProperty("deviceSid", sid)
.build();
thingDiscovered(discoveryResult);
}
});
stopScan();
} catch (MediolaAioCommandError|MediolaAioCommunicationError ignored) {
}
logger.debug("Starting AIO Gateway discovery scan");
}
@Override
public synchronized void stopScan() {
logger.debug("Stopping AIO Gateway discovery scan");
super.stopScan();
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.exception;
/**
*
* @author Av3m - Initial contribution
*/
public class MediolaAioCommandError extends Exception {
public MediolaAioCommandError(String message, Throwable err) {
super(message, err);
}
public MediolaAioCommandError(String message) {
super(message);
}
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.exception;
/**
* @author Av3m - Initial contribution
*/
public class MediolaAioCommunicationError extends Exception {
public MediolaAioCommunicationError(String message, Throwable err) {
super(message, err);
}
public MediolaAioCommunicationError(String message) {
super(message);
}
}

View File

@ -0,0 +1,244 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.handler;
import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*;
import com.google.gson.JsonElement;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.mediolaaiogateway.internal.config.MediolaAioEleroDeviceConfig;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommandError;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommunicationError;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioCommand;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioEleroCommand;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioType;
import org.openhab.core.library.types.*;
import org.openhab.core.thing.*;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* The {@link MediolaAioEleroDeviceHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
private static final Map<String, Integer> _commandMap = new HashMap<>();
private final Logger logger = LoggerFactory.getLogger(MediolaAioEleroDeviceHandler.class);
private @Nullable MediolaAioEleroDeviceConfig config;
private @Nullable ScheduledFuture<?> pollingJob;
private HttpClient _httpClient;
public MediolaAioEleroDeviceHandler(Thing thing, HttpClient httpClient) {
super(thing);
_httpClient = httpClient;
}
static {
_commandMap.put("ON", AioEleroCommand.UpOn);
_commandMap.put("OFF", AioEleroCommand.DownOff);
_commandMap.put("UP", AioEleroCommand.UpOn);
_commandMap.put("DOWN", AioEleroCommand.DownOff);
_commandMap.put("STOP", AioEleroCommand.Stop);
_commandMap.put("DOWN_3S", AioEleroCommand.Down3s);
_commandMap.put("UP_3S", AioEleroCommand.Up3s);
_commandMap.put("DOWN_STEP_BIT", AioEleroCommand.DownStepBit);
_commandMap.put("UP_STEP_BIT", AioEleroCommand.UpStepBit);
_commandMap.put("DOUBLE_TAP_DOWN", AioEleroCommand.DoubleTapDown);
_commandMap.put("DOUBLE_TAP_UP", AioEleroCommand.DoubleTapUp);
_commandMap.put("START_LEARNING", AioEleroCommand.StartLearning);
_commandMap.put("ON_PULSE_MOVE", AioEleroCommand.OnPulseMove);
_commandMap.put("OFF_PULSE_MOVE", AioEleroCommand.OffPulseMove);
_commandMap.put("AS_CLOSE", AioEleroCommand.ASClose);
_commandMap.put("AS_MOVE", AioEleroCommand.ASMove);
_commandMap.put("SAVE_INTERMEDIATE", AioEleroCommand.SaveIntermediatePos);
_commandMap.put("SAVE_VENTILATION", AioEleroCommand.SaveVentilationPos);
}
public void stateUpdate(ChannelUID channelUID) {
@Nullable Bridge bridge = getBridge();
if ( bridge == null) {
logger.warn("could not get bridge");
return;
}
@Nullable MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler();
if ( bHandler == null || config == null) { return; }
Map<String, String> params = new HashMap<>();
params.put("adr", String.format("%02x", config.deviceAddress));
params.put("type", AioType.Elero);
try {
JsonElement jsonElem = bHandler.sendCommand(AioCommand.RefreshSC, params);
String strState = jsonElem.getAsJsonObject().get("state").getAsString();
int state = Integer.parseInt(strState, 16) & 0x00FF;
updateState(channelUID, new StringType(Integer.toString(state)));
} catch (MediolaAioCommandError | MediolaAioCommunicationError err) {
logger.error("{}",err.getMessage());
}
}
private void handleCommandString(String cmd) {
@Nullable Bridge bridge = getBridge();
@Nullable MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler();
if ( bHandler == null || config == null) { return; }
Integer eleroCmd = _commandMap.get(cmd);
if (eleroCmd != null ) {
try {
bHandler.sendEleroCommand(config.deviceAddress, eleroCmd);
} catch (MediolaAioCommandError mediolaAioCommandError) {
logger.error("Command not successful: {}", mediolaAioCommandError.getMessage());
} catch (MediolaAioCommunicationError mediolaAioCommunicationError) {
updateStatus(
ThingStatus.OFFLINE,
ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Could not reach Mediola AIO Gateway"));
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
String id = channelUID.getId();
if (CHANNEL_CONTROL.equals(id)) {
if (command instanceof StringType) {
String strCommand = ((StringType) command).toString();
handleCommandString(strCommand);
}
}
else if (CHANNEL_CONTROL_BLIND.equals(id)) {
if (command instanceof UpDownType) {
handleCommandString(((UpDownType) command).toString());
}
else if (command instanceof StopMoveType) {
handleCommandString(((StopMoveType) command).toString());
}
else if (command instanceof PercentType) {
int val = ((PercentType) command).intValue();
if (val == 0 ) {
//open position
handleCommandString("UP");
}
if ( 0 < val && val <= 50 ) {
//intermediate position
handleCommandString("DOUBLE_TAP_DOWN");
}
if ( 50 < val && val <= 100 ) {
//ventilation position
handleCommandString("DOUBLE_TAP_UP");
}
if ( val == 100 ) {
//close position
handleCommandString("DOWN");
}
}
}
else if (CHANNEL_CONTROL_SWITCH.equals(id)) {
if (command instanceof OnOffType) {
handleCommandString(((OnOffType) command).toString());
}
}
else if (CHANNEL_STATE.equals(id)) {
if (command instanceof RefreshType) {
stateUpdate(channelUID);
}
}
}
@Override
public void dispose() {
if (pollingJob != null ) {
pollingJob.cancel(true);
pollingJob = null;
}
super.dispose();
}
@Override
public void initialize() {
config = getConfigAs(MediolaAioEleroDeviceConfig.class);
if (config == null) {
throw new RuntimeException("config object was expected!");
}
// TODO: Initialize the handler.
// The framework requires you to return from this method quickly. Also, before leaving this method a thing
// status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in
// case you can decide it directly.
// In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN
// access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the
// background.
// set the thing status to UNKNOWN temporarily and let the background task decide for the real status.
// the framework is then able to reuse the resources from the thing handler initialization.
// we set this upfront to reliably check status updates in unit tests.
updateStatus(ThingStatus.UNKNOWN);
pollingJob = scheduler.scheduleWithFixedDelay(() -> {
@Nullable Channel ch = thing.getChannel(CHANNEL_STATE);
if ( ch == null ) {return; }
this.stateUpdate(ch.getUID());
}, 0, config.refreshTime, TimeUnit.SECONDS);
// Example for background initialization:
scheduler.execute(() -> {
boolean thingReachable = true; // <background task with long running initialization here>
// when done do:
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
});
// These logging types should be primarily used by bindings
// logger.trace("Example trace message");
// logger.debug("Example debug message");
// logger.warn("Example warn message");
// Note: When initialization can NOT be done set the status with more details for further
// analysis. See also class ThingStatusDetail for all available status details.
// Add a description to give user information to understand why thing does not work as expected. E.g.
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
// "Can not access device as username and/or password are invalid");
}
}

View File

@ -0,0 +1,163 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.handler;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpVersion;
import org.openhab.binding.mediolaaiogateway.internal.config.MediolaAioBridgeConfig;
import org.openhab.binding.mediolaaiogateway.internal.discovery.MediolaAioDeviceDiscoveryService;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommandError;
import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommunicationError;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioCommand;
import org.openhab.binding.mediolaaiogateway.internal.mediolaapi.AioType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.HttpMethod;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* The {@link MediolaAioGatewayBridgeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class MediolaAioGatewayBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(MediolaAioGatewayBridgeHandler.class);
private HttpClient _httpClient;
private @Nullable MediolaAioBridgeConfig config;
public MediolaAioGatewayBridgeHandler(Bridge thing, HttpClient httpClient) {
super(thing);
_httpClient = httpClient;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(MediolaAioDeviceDiscoveryService.class);
}
public JsonElement sendEleroCommand(Integer deviceAddress, Integer cmd) throws MediolaAioCommandError, MediolaAioCommunicationError {
Map<String, String> params = new HashMap<>();
params.put("type", AioType.Elero);
params.put("data", String.format("%02x%02x", deviceAddress, cmd));
return this.sendCommand(AioCommand.SendSC, params);
}
public JsonElement sendCommand(String command, Map<String, String> params) throws MediolaAioCommandError, MediolaAioCommunicationError {
Request request = _httpClient.newRequest(config.gatewayAddress, config.gatewayPort)
.scheme("http")
.agent("OpenHAB Mediola AIO Gateway Binding")
.version(HttpVersion.HTTP_1_1)
.method(HttpMethod.GET)
.path("/cmd")
.param("XC_FNC", command)
.timeout(5, TimeUnit.SECONDS);
params.forEach(request::param);
@Nullable ContentResponse response;
byte[] content;
synchronized (this) {
try {
response = request.send();
content = response.getContent();
} catch (InterruptedException | TimeoutException | ExecutionException e) {
throw new MediolaAioCommunicationError("communication error", e);
}
}
JsonObject convertedObject = new Gson().fromJson(new String(content), JsonObject.class);
if (!convertedObject.has("XC_SUC")) {
if (convertedObject.has("XC_ERR")) {
throw new MediolaAioCommandError(convertedObject.get("XC_ERR").getAsString());
} else {
throw new MediolaAioCommandError("unknown reason.");
}
}
return convertedObject.get("XC_SUC");
}
@Override
public void initialize() {
config = getConfigAs(MediolaAioBridgeConfig.class);
// TODO: Initialize the handler.
// The framework requires you to return from this method quickly. Also, before leaving this method a thing
// status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in
// case you can decide it directly.
// In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN
// access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the
// background.
// set the thing status to UNKNOWN temporarily and let the background task decide for the real status.
// the framework is then able to reuse the resources from the thing handler initialization.
// we set this upfront to reliably check status updates in unit tests.
updateStatus(ThingStatus.UNKNOWN);
// Example for background initialization:
scheduler.execute(() -> {
boolean thingReachable = true; // <background task with long running initialization here>
// when done do:
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
});
// These logging types should be primarily used by bindings
// logger.trace("Example trace message");
// logger.debug("Example debug message");
// logger.warn("Example warn message");
// Note: When initialization can NOT be done set the status with more details for further
// analysis. See also class ThingStatusDetail for all available status details.
// Add a description to give user information to understand why thing does not work as expected. E.g.
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
// "Can not access device as username and/or password are invalid");
}
}

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.mediolaaiogateway.internal; package org.openhab.binding.mediolaaiogateway.internal.handler;
import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*; import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*;
@ -18,12 +18,17 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; 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.Bridge;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory; 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.Component;
import org.osgi.service.component.annotations.Reference;
/** /**
* The {@link MediolaAioGatewayHandlerFactory} is responsible for creating things and thing * The {@link MediolaAioGatewayHandlerFactory} is responsible for creating things and thing
@ -35,7 +40,14 @@ import org.osgi.service.component.annotations.Component;
@Component(configurationPid = "binding.mediolaaiogateway", service = ThingHandlerFactory.class) @Component(configurationPid = "binding.mediolaaiogateway", service = ThingHandlerFactory.class)
public class MediolaAioGatewayHandlerFactory extends BaseThingHandlerFactory { public class MediolaAioGatewayHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SAMPLE); private final HttpClient httpClient;
@Activate
public MediolaAioGatewayHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
httpClient = httpClientFactory.getCommonHttpClient();
}
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_ELERO_DEVICE);
@Override @Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) { public boolean supportsThingType(ThingTypeUID thingTypeUID) {
@ -46,8 +58,10 @@ public class MediolaAioGatewayHandlerFactory extends BaseThingHandlerFactory {
protected @Nullable ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_SAMPLE.equals(thingTypeUID)) { if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
return new MediolaAioGatewayHandler(thing); return new MediolaAioGatewayBridgeHandler((Bridge) thing, httpClient);
} else if (THING_TYPE_ELERO_DEVICE.equals(thingTypeUID)) {
return new MediolaAioEleroDeviceHandler(thing, httpClient);
} }
return null; return null;

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.mediolaapi;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
*
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class AioCommand {
public static final String RefreshSC = "RefreshSC";
public static final String SendSC = "SendSC";
public static final String GetStates = "GetStates";
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.mediolaapi;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class AioEleroCommand {
public static final Integer DownOff = 0x00;
public static final Integer UpOn = 0x01;
public static final Integer Stop = 0x02;
public static final Integer UpStepBit = 0x03;
public static final Integer DownStepBit = 0x04;
public static final Integer ManuMode = 0x05;
public static final Integer AutoMode = 0x06;
public static final Integer ToggleMode = 0x07;
public static final Integer Up3s = 0x08;
public static final Integer Down3s = 0x09;
public static final Integer DoubleTapUp = 0x0A;
public static final Integer DoubleTapDown = 0x0B;
public static final Integer StartLearning = 0x0C;
public static final Integer OnPulseMove = 0x0D;
public static final Integer OffPulseMove = 0x0E;
public static final Integer ASClose = 0x0F;
public static final Integer ASMove = 0x10;
public static final Integer SaveVentilationPos = 0x14;
public static final Integer SaveIntermediatePos = 0x12;
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 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.mediolaaiogateway.internal.mediolaapi;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class AioType {
public static final String Elero = "ER";
}

View File

@ -4,6 +4,6 @@
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd"> xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>MediolaAioGateway Binding</name> <name>MediolaAioGateway Binding</name>
<description>This is the binding for MediolaAioGateway.</description> <description>This is an inofficial and work-in-progress binding for Mediola AIO Gateway.</description>
</binding:binding> </binding:binding>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mediolaaiogateway" 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">
<bridge-type id="bridge">
<label>Mediola AIO Gateway Bridge</label>
<description>The Mediola AIO Gateway bridge represents a Mediola AIO gateway</description>
<config-description>
<parameter name="gatewayAddress" type="text" required="true">
<context>network-address</context>
<label>Gateway Address</label>
<description>Network address of the Homematic gateway</description>
</parameter>
<parameter name="gatewayPort" type="integer">
<label>Gateway Port</label>
<description/>
<default>80</default>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@ -1,46 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mediolaaiogateway" <thing:thing-descriptions bindingId="mediolaaiogateway" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
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">
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="elero-device">
<thing-type id="sample">
<!-- Comment this in for Things which need a Bridge to function.
<supported-bridge-type-refs> <supported-bridge-type-refs>
<bridge-type-ref id="MediolaAioGatewayBridge" /> <bridge-type-ref id="bridge" />
</supported-bridge-type-refs> </supported-bridge-type-refs>
--> <label>Elero Device</label>
<description>Elero Device (via AIO Gateway)</description>
<label>MediolaAioGateway Binding Thing</label>
<description>Sample thing for MediolaAioGateway Binding</description>
<channels> <channels>
<channel id="channel1" typeId="sample-channel"/> <channel id="control" typeId="elero-control" />
<channel id="state" typeId="elero-state" />
<channel id="control-blind" typeId="elero-control-blind" />
<channel id="control-switch" typeId="elero-control-switch" />
</channels> </channels>
<config-description> <config-description>
<parameter name="hostname" type="text" required="true"> <parameter name="deviceAddress" type="integer">
<context>network-address</context> <label>Device Address</label>
<label>Hostname</label> <description/>
<description>Hostname or IP address of the device</description>
</parameter> </parameter>
<parameter name="password" type="text" required="true"> <parameter name="deviceSid" type="integer">
<context>password</context> <label>Device SID</label>
<label>Password</label> <description/>
<description>Passwort to access the device</description>
</parameter> </parameter>
<parameter name="refreshInterval" type="integer" unit="s" min="1"> <parameter name="refreshTime" type="integer">
<label>Refresh Interval</label> <label>Refresh time in seconds</label>
<description>Interval the device is polled in sec.</description> <description>cycle time for status updates</description>
<default>10</default>
</parameter> </parameter>
</config-description> </config-description>
</thing-type>
<!-- Sample Channel Type --> </thing-type>
<channel-type id="sample-channel"> <channel-type id="elero-state">
<item-type>Number:Temperature</item-type> <item-type>String</item-type>
<label>Example Temperature</label><!-- Use uppercase words, except prepositions. 2-3 words, max 25 chars --> <label>Device State</label>
<description>Sample channel for MediolaAioGateway Binding</description> <state readOnly="true">
<options>
<option value="0">no information</option>
<option value="1">top position stop</option>
<option value="2">bottom position stop</option>
<option value="3">intermediate position stop</option>
<option value="4">tilt position stop</option>
<option value="5">blocking stop</option>
<option value="6">overheated stop</option>
<option value="7">timeout stop</option>
<option value="8">start to move up</option>
<option value="9">start to move down</option>
<option value="10">moving up</option>
<option value="11">moving down</option>
<option value="13">stopped in undefined position</option>
<option value="14">top position stop</option>
<option value="15">bottom position stop</option>
</options>
</state>
</channel-type> </channel-type>
<channel-type id="elero-control-blind">
<item-type>Rollershutter</item-type>
<label>Control Elero Rollershutter</label>
</channel-type>
<channel-type id="elero-control-switch">
<item-type>Switch</item-type>
<label>Control Elero Switch</label>
</channel-type>
<channel-type id="elero-control">
<item-type>String</item-type>
<label>Command</label>
<command>
<options>
<option value="ON">on</option>
<option value="OFF">off</option>
<option value="DOWN">down</option>
<option value="UP">up</option>
<option value="STOP">stop</option>
<option value="UP_3S">up 3sec</option>
<option value="DOWN_3S">down 3sec</option>
<option value="UP_STEP_BIT">up step bit</option>
<option value="DOWN_STEP_BIT">down step bit</option>
<option value="DOUBLE_TAP_DOWN">double tap down</option>
<option value="DOUBLE_TAP_UP">double tap up</option>
<option value="START_LEARNING">start learning</option>
<option value="ON_PULSE_MOVE">on pulse move</option>
<option value="OFF_PULSE_MOVE">off pulse move</option>
<option value="AS_CLOSE">AS CLOSE</option>
<option value="AS_MOVE">AS MOVE</option>
<option value="SAVE_INTERMEDIATE">save intermediate pos</option>
<option value="SAVE_VENTILATION">save ventilation pos</option>
</options>
</command>
</channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -229,6 +229,7 @@
<module>org.openhab.binding.mcp23017</module> <module>org.openhab.binding.mcp23017</module>
<module>org.openhab.binding.meater</module> <module>org.openhab.binding.meater</module>
<module>org.openhab.binding.mecmeter</module> <module>org.openhab.binding.mecmeter</module>
<module>org.openhab.binding.mediolaaiogateway</module>
<module>org.openhab.binding.melcloud</module> <module>org.openhab.binding.melcloud</module>
<module>org.openhab.binding.mercedesme</module> <module>org.openhab.binding.mercedesme</module>
<module>org.openhab.binding.meteoalerte</module> <module>org.openhab.binding.meteoalerte</module>