adapt to 4.1.x

This commit is contained in:
Thomas Vogl 2024-01-04 12:20:57 +01:00
parent af2f10bde8
commit 630620edbd
11 changed files with 114 additions and 115 deletions

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId> <artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.2.0</version> <version>4.1.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>org.openhab.binding.mediolaaiogateway</artifactId> <artifactId>org.openhab.binding.mediolaaiogateway</artifactId>

View File

@ -12,8 +12,6 @@
*/ */
package org.openhab.binding.mediolaaiogateway.internal.config; package org.openhab.binding.mediolaaiogateway.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link MediolaAioBridgeConfig} class contains fields mapping thing configuration parameters. * The {@link MediolaAioBridgeConfig} class contains fields mapping thing configuration parameters.
* *

View File

@ -12,8 +12,12 @@
*/ */
package org.openhab.binding.mediolaaiogateway.internal.discovery; package org.openhab.binding.mediolaaiogateway.internal.discovery;
import com.google.gson.JsonElement; import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.THING_TYPE_ELERO_DEVICE;
import com.google.gson.JsonObject;
import java.util.HashMap;
import java.util.Map;
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.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommandError; import org.openhab.binding.mediolaaiogateway.internal.exception.MediolaAioCommandError;
@ -30,11 +34,8 @@ import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap; import com.google.gson.JsonElement;
import java.util.Map; import com.google.gson.JsonObject;
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. * The {@link MediolaAioDeviceDiscoveryService} is used to discover devices that are connected to a Homematic gateway.
@ -42,15 +43,11 @@ import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBi
* @author Gerhard Riegler - Initial contribution * @author Gerhard Riegler - Initial contribution
*/ */
public class MediolaAioDeviceDiscoveryService extends AbstractDiscoveryService public class MediolaAioDeviceDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(MediolaAioDeviceDiscoveryService.class); private final Logger logger = LoggerFactory.getLogger(MediolaAioDeviceDiscoveryService.class);
private static final int DISCOVER_TIMEOUT_SECONDS = 300; private static final int DISCOVER_TIMEOUT_SECONDS = 300;
private @NonNullByDefault({}) private @NonNullByDefault({}) MediolaAioGatewayBridgeHandler bridgeHandler;
MediolaAioGatewayBridgeHandler bridgeHandler;
public MediolaAioDeviceDiscoveryService() { public MediolaAioDeviceDiscoveryService() {
super(Set.of(THING_TYPE_ELERO_DEVICE), DISCOVER_TIMEOUT_SECONDS, false); super(Set.of(THING_TYPE_ELERO_DEVICE), DISCOVER_TIMEOUT_SECONDS, false);
@ -89,20 +86,19 @@ public class MediolaAioDeviceDiscoveryService extends AbstractDiscoveryService
JsonElement devs = bridgeHandler.sendCommand(AioCommand.GetStates, params); JsonElement devs = bridgeHandler.sendCommand(AioCommand.GetStates, params);
devs.getAsJsonArray().forEach((e) -> { devs.getAsJsonArray().forEach((e) -> {
JsonObject o = e.getAsJsonObject(); JsonObject o = e.getAsJsonObject();
if ( o.has("type") && o.get("type").getAsString().equals(AioType.Elero)) { if (o.has("type") && o.get("type").getAsString().equals(AioType.Elero)) {
int address = Integer.parseInt(o.get("adr").getAsString(),16); int address = Integer.parseInt(o.get("adr").getAsString(), 16);
int sid = Integer.parseInt(o.get("sid").getAsString(),16); int sid = Integer.parseInt(o.get("sid").getAsString(), 16);
String strAddress = String.format("%02X", address); String strAddress = String.format("%02X", address);
ThingUID thingUID = new ThingUID(THING_TYPE_ELERO_DEVICE, bridgeHandler.getThing().getUID(), strAddress); ThingUID thingUID = new ThingUID(THING_TYPE_ELERO_DEVICE, bridgeHandler.getThing().getUID(),
strAddress);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withBridge(bridgeHandler.getThing().getUID()) .withBridge(bridgeHandler.getThing().getUID())
.withLabel(String.format("Elero Device %s", strAddress)) .withLabel(String.format("Elero Device %s", strAddress))
.withProperty("deviceAddress", address) .withProperty("deviceAddress", address).withProperty("deviceSid", sid).build();
.withProperty("deviceSid", sid)
.build();
thingDiscovered(discoveryResult); thingDiscovered(discoveryResult);
} }
@ -110,7 +106,7 @@ public class MediolaAioDeviceDiscoveryService extends AbstractDiscoveryService
stopScan(); stopScan();
} catch (MediolaAioCommandError|MediolaAioCommunicationError ignored) { } catch (MediolaAioCommandError | MediolaAioCommunicationError ignored) {
} }
logger.debug("Starting AIO Gateway discovery scan"); logger.debug("Starting AIO Gateway discovery scan");

View File

@ -14,7 +14,11 @@ package org.openhab.binding.mediolaaiogateway.internal.handler;
import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*; import static org.openhab.binding.mediolaaiogateway.internal.MediolaAioGatewayBindingConstants.*;
import com.google.gson.JsonElement; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
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.eclipse.jetty.client.HttpClient;
@ -32,10 +36,7 @@ import org.openhab.core.types.RefreshType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap; import com.google.gson.JsonElement;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/** /**
* The {@link MediolaAioEleroDeviceHandler} is responsible for handling commands, which are * The {@link MediolaAioEleroDeviceHandler} is responsible for handling commands, which are
@ -46,7 +47,6 @@ import java.util.concurrent.TimeUnit;
@NonNullByDefault @NonNullByDefault
public class MediolaAioEleroDeviceHandler extends BaseThingHandler { public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
private static final Map<String, Integer> _commandMap = new HashMap<>(); private static final Map<String, Integer> _commandMap = new HashMap<>();
private final Logger logger = LoggerFactory.getLogger(MediolaAioEleroDeviceHandler.class); private final Logger logger = LoggerFactory.getLogger(MediolaAioEleroDeviceHandler.class);
@ -82,14 +82,18 @@ public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
} }
public void stateUpdate(ChannelUID channelUID) { public void stateUpdate(ChannelUID channelUID) {
@Nullable Bridge bridge = getBridge(); @Nullable
if ( bridge == null) { Bridge bridge = getBridge();
if (bridge == null) {
logger.warn("could not get bridge"); logger.warn("could not get bridge");
return; return;
} }
@Nullable MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler(); @Nullable
MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler();
if ( bHandler == null || config == null) { return; } if (bHandler == null || config == null) {
return;
}
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
params.put("adr", String.format("%02x", config.deviceAddress)); params.put("adr", String.format("%02x", config.deviceAddress));
@ -102,27 +106,29 @@ public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
updateState(channelUID, new StringType(Integer.toString(state))); updateState(channelUID, new StringType(Integer.toString(state)));
} catch (MediolaAioCommandError | MediolaAioCommunicationError err) { } catch (MediolaAioCommandError | MediolaAioCommunicationError err) {
logger.error("{}",err.getMessage()); logger.error("{}", err.getMessage());
} }
} }
private void handleCommandString(String cmd) { private void handleCommandString(String cmd) {
@Nullable Bridge bridge = getBridge(); @Nullable
@Nullable MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler(); Bridge bridge = getBridge();
@Nullable
MediolaAioGatewayBridgeHandler bHandler = (MediolaAioGatewayBridgeHandler) bridge.getHandler();
if ( bHandler == null || config == null) { return; } if (bHandler == null || config == null) {
return;
}
Integer eleroCmd = _commandMap.get(cmd); Integer eleroCmd = _commandMap.get(cmd);
if (eleroCmd != null ) { if (eleroCmd != null) {
try { try {
bHandler.sendEleroCommand(config.deviceAddress, eleroCmd); bHandler.sendEleroCommand(config.deviceAddress, eleroCmd);
} catch (MediolaAioCommandError mediolaAioCommandError) { } catch (MediolaAioCommandError mediolaAioCommandError) {
logger.error("Command not successful: {}", mediolaAioCommandError.getMessage()); logger.error("Command not successful: {}", mediolaAioCommandError.getMessage());
} catch (MediolaAioCommunicationError mediolaAioCommunicationError) { } catch (MediolaAioCommunicationError mediolaAioCommunicationError) {
updateStatus( updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
ThingStatus.OFFLINE,
ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Could not reach Mediola AIO Gateway")); String.format("Could not reach Mediola AIO Gateway"));
} }
} }
@ -142,26 +148,24 @@ public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
else if (CHANNEL_CONTROL_BLIND.equals(id)) { else if (CHANNEL_CONTROL_BLIND.equals(id)) {
if (command instanceof UpDownType) { if (command instanceof UpDownType) {
handleCommandString(((UpDownType) command).toString()); handleCommandString(((UpDownType) command).toString());
} } else if (command instanceof StopMoveType) {
else if (command instanceof StopMoveType) {
handleCommandString(((StopMoveType) command).toString()); handleCommandString(((StopMoveType) command).toString());
} } else if (command instanceof PercentType) {
else if (command instanceof PercentType) {
int val = ((PercentType) command).intValue(); int val = ((PercentType) command).intValue();
if (val == 0 ) { if (val == 0) {
//open position // open position
handleCommandString("UP"); handleCommandString("UP");
} }
if ( 0 < val && val <= 50 ) { if (0 < val && val <= 50) {
//intermediate position // intermediate position
handleCommandString("DOUBLE_TAP_DOWN"); handleCommandString("DOUBLE_TAP_DOWN");
} }
if ( 50 < val && val <= 100 ) { if (50 < val && val <= 100) {
//ventilation position // ventilation position
handleCommandString("DOUBLE_TAP_UP"); handleCommandString("DOUBLE_TAP_UP");
} }
if ( val == 100 ) { if (val == 100) {
//close position // close position
handleCommandString("DOWN"); handleCommandString("DOWN");
} }
} }
@ -182,7 +186,7 @@ public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
@Override @Override
public void dispose() { public void dispose() {
if (pollingJob != null ) { if (pollingJob != null) {
pollingJob.cancel(true); pollingJob.cancel(true);
pollingJob = null; pollingJob = null;
} }
@ -212,8 +216,11 @@ public class MediolaAioEleroDeviceHandler extends BaseThingHandler {
updateStatus(ThingStatus.UNKNOWN); updateStatus(ThingStatus.UNKNOWN);
pollingJob = scheduler.scheduleWithFixedDelay(() -> { pollingJob = scheduler.scheduleWithFixedDelay(() -> {
@Nullable Channel ch = thing.getChannel(CHANNEL_STATE); @Nullable
if ( ch == null ) {return; } Channel ch = thing.getChannel(CHANNEL_STATE);
if (ch == null) {
return;
}
this.stateUpdate(ch.getUID()); this.stateUpdate(ch.getUID());

View File

@ -12,9 +12,16 @@
*/ */
package org.openhab.binding.mediolaaiogateway.internal.handler; package org.openhab.binding.mediolaaiogateway.internal.handler;
import com.google.gson.Gson; import java.util.Collection;
import com.google.gson.JsonElement; import java.util.Collections;
import com.google.gson.JsonObject; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.ws.rs.HttpMethod;
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.eclipse.jetty.client.HttpClient;
@ -37,14 +44,9 @@ import org.openhab.core.types.Command;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.ws.rs.HttpMethod; import com.google.gson.Gson;
import java.util.Collection; import com.google.gson.JsonElement;
import java.util.Collections; import com.google.gson.JsonObject;
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 * The {@link MediolaAioGatewayBridgeHandler} is responsible for handling commands, which are
@ -75,34 +77,32 @@ public class MediolaAioGatewayBridgeHandler extends BaseBridgeHandler {
return Collections.singleton(MediolaAioDeviceDiscoveryService.class); return Collections.singleton(MediolaAioDeviceDiscoveryService.class);
} }
public JsonElement sendEleroCommand(Integer deviceAddress, Integer cmd) throws MediolaAioCommandError, MediolaAioCommunicationError { public JsonElement sendEleroCommand(Integer deviceAddress, Integer cmd)
throws MediolaAioCommandError, MediolaAioCommunicationError {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
params.put("type", AioType.Elero); params.put("type", AioType.Elero);
params.put("data", String.format("%02x%02x", deviceAddress, cmd)); params.put("data", String.format("%02x%02x", deviceAddress, cmd));
return this.sendCommand(AioCommand.SendSC, params); return this.sendCommand(AioCommand.SendSC, params);
} }
public JsonElement sendCommand(String command, Map<String, String> params) throws MediolaAioCommandError, MediolaAioCommunicationError { public JsonElement sendCommand(String command, Map<String, String> params)
throws MediolaAioCommandError, MediolaAioCommunicationError {
Request request = _httpClient.newRequest(config.gatewayAddress, config.gatewayPort) Request request = _httpClient.newRequest(config.gatewayAddress, config.gatewayPort).scheme("http")
.scheme("http") .agent("OpenHAB Mediola AIO Gateway Binding").version(HttpVersion.HTTP_1_1).method(HttpMethod.GET)
.agent("OpenHAB Mediola AIO Gateway Binding") .path("/cmd").param("XC_FNC", command).timeout(5, TimeUnit.SECONDS);
.version(HttpVersion.HTTP_1_1)
.method(HttpMethod.GET)
.path("/cmd")
.param("XC_FNC", command)
.timeout(5, TimeUnit.SECONDS);
params.forEach(request::param); params.forEach(request::param);
@Nullable ContentResponse response; @Nullable
ContentResponse response;
byte[] content; byte[] content;
synchronized (this) { synchronized (this) {
try { try {
response = request.send(); response = request.send();
if(!HttpStatus.isSuccess(response.getStatus())) { if (!HttpStatus.isSuccess(response.getStatus())) {
throw new MediolaAioCommunicationError("invalid http result code"); throw new MediolaAioCommunicationError("invalid http result code");
} }
content = response.getContent(); content = response.getContent();
@ -127,7 +127,6 @@ public class MediolaAioGatewayBridgeHandler extends BaseBridgeHandler {
} }
} }
@Override @Override
public void initialize() { public void initialize() {
config = getConfigAs(MediolaAioBridgeConfig.class); config = getConfigAs(MediolaAioBridgeConfig.class);

View File

@ -47,7 +47,8 @@ public class MediolaAioGatewayHandlerFactory extends BaseThingHandlerFactory {
httpClient = httpClientFactory.getCommonHttpClient(); httpClient = httpClientFactory.getCommonHttpClient();
} }
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_ELERO_DEVICE); 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) {

View File

@ -12,7 +12,6 @@
*/ */
package org.openhab.binding.mediolaaiogateway.internal.mediolaapi; package org.openhab.binding.mediolaaiogateway.internal.mediolaapi;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
@ -20,24 +19,23 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/ */
@NonNullByDefault @NonNullByDefault
public class AioEleroCommand { public class AioEleroCommand {
public static final Integer DownOff = 0x00; public static final Integer DownOff = 0x00;
public static final Integer UpOn = 0x01; public static final Integer UpOn = 0x01;
public static final Integer Stop = 0x02; public static final Integer Stop = 0x02;
public static final Integer UpStepBit = 0x03; public static final Integer UpStepBit = 0x03;
public static final Integer DownStepBit = 0x04; public static final Integer DownStepBit = 0x04;
public static final Integer ManuMode = 0x05; public static final Integer ManuMode = 0x05;
public static final Integer AutoMode = 0x06; public static final Integer AutoMode = 0x06;
public static final Integer ToggleMode = 0x07; public static final Integer ToggleMode = 0x07;
public static final Integer Up3s = 0x08; public static final Integer Up3s = 0x08;
public static final Integer Down3s = 0x09; public static final Integer Down3s = 0x09;
public static final Integer DoubleTapUp = 0x0A; public static final Integer DoubleTapUp = 0x0A;
public static final Integer DoubleTapDown = 0x0B; public static final Integer DoubleTapDown = 0x0B;
public static final Integer StartLearning = 0x0C; public static final Integer StartLearning = 0x0C;
public static final Integer OnPulseMove = 0x0D; public static final Integer OnPulseMove = 0x0D;
public static final Integer OffPulseMove = 0x0E; public static final Integer OffPulseMove = 0x0E;
public static final Integer ASClose = 0x0F; public static final Integer ASClose = 0x0F;
public static final Integer ASMove = 0x10; public static final Integer ASMove = 0x10;
public static final Integer SaveVentilationPos = 0x14; public static final Integer SaveVentilationPos = 0x14;
public static final Integer SaveIntermediatePos = 0x12; public static final Integer SaveIntermediatePos = 0x12;
} }

View File

@ -12,8 +12,6 @@
*/ */
package org.openhab.binding.mediolaaiogateway.internal.mediolaapi; package org.openhab.binding.mediolaaiogateway.internal.mediolaapi;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mediolaaiogateway" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <thing:thing-descriptions bindingId="mediolaaiogateway"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> 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"> <bridge-type id="bridge">

View File

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mediolaaiogateway" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <thing:thing-descriptions bindingId="mediolaaiogateway"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> 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">
<thing-type id="elero-device"> <thing-type id="elero-device">
<supported-bridge-type-refs> <supported-bridge-type-refs>
<bridge-type-ref id="bridge" /> <bridge-type-ref id="bridge"/>
</supported-bridge-type-refs> </supported-bridge-type-refs>
<label>Elero Device</label> <label>Elero Device</label>
<description>Elero Device (via AIO Gateway)</description> <description>Elero Device (via AIO Gateway)</description>
<channels> <channels>
<channel id="control" typeId="elero-control" /> <channel id="control" typeId="elero-control"/>
<channel id="state" typeId="elero-state" /> <channel id="state" typeId="elero-state"/>
<channel id="control-blind" typeId="elero-control-blind" /> <channel id="control-blind" typeId="elero-control-blind"/>
<channel id="control-switch" typeId="elero-control-switch" /> <channel id="control-switch" typeId="elero-control-switch"/>
</channels> </channels>
<config-description> <config-description>
<parameter name="deviceAddress" type="integer"> <parameter name="deviceAddress" type="integer">

View File

@ -229,7 +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.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>