[shelly] Adapt shelly v1 handler to californium 4.0.0-M6 (#20704)

* [shelly] Adapt shelly v1 handler to californium 4.0.0-M6

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
This commit is contained in:
Holger Friedrich
2026-05-12 17:14:31 +02:00
committed by GitHub
parent 01547833fb
commit d90d079510
2 changed files with 83 additions and 38 deletions
@@ -33,6 +33,8 @@ import org.eclipse.californium.core.coap.Option;
import org.eclipse.californium.core.coap.OptionNumberRegistry;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.coap.option.IntegerOption;
import org.eclipse.californium.core.coap.option.StringOption;
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -182,14 +184,16 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
// We can't identify device by IP, so we need to check the CoAP header's Global Device ID
for (Option opt : options) {
if (opt.getNumber() == COIOT_OPTION_GLOBAL_DEVID) {
String devid = opt.getStringValue();
if (devid.contains("#") && profile.device.mac != null) {
// Format: <device type>#<mac address>#<coap version>
String macid = substringBetween(devid, "#", "#");
if (getString(profile.device.mac).toUpperCase(Locale.ROOT)
.contains(macid.toUpperCase(Locale.ROOT))) {
match = true;
break;
if (opt instanceof StringOption strOpt) {
String devid = strOpt.getStringValue();
if (devid.contains("#") && profile.device.mac != null) {
// Format: <device type>#<mac address>#<coap version>
String macid = substringBetween(devid, "#", "#");
if (getString(profile.device.mac).toUpperCase(Locale.ROOT)
.contains(macid.toUpperCase(Locale.ROOT))) {
match = true;
break;
}
}
}
}
@@ -226,52 +230,67 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
for (Option opt : options) {
switch (opt.getNumber()) {
case OptionNumberRegistry.URI_PATH:
uri = COLOIT_URI_BASE + opt.getStringValue();
if (opt instanceof StringOption strOpt) {
uri = COLOIT_URI_BASE + strOpt.getStringValue();
} else {
logger.debug("{}: URI_PATH option is not a StringOption, type: {}", thingName,
opt.getClass().getSimpleName());
}
break;
case OptionNumberRegistry.URI_HOST: // ignore
break;
case OptionNumberRegistry.CONTENT_FORMAT: // ignore
break;
case COIOT_OPTION_GLOBAL_DEVID:
devId = opt.getStringValue();
String sVersion = substringAfterLast(devId, "#");
int iVersion;
try {
iVersion = Integer.parseInt(sVersion);
} catch (NumberFormatException e) {
logger.debug("{}: Unable to parse version in CoIoT message: {}", thingName, devId);
thingHandler.incProtErrors();
return;
}
if (coiotBound && coiotVers != iVersion) {
logger.debug("{}: CoIoT version has changed from {} to {}, maybe the firmware was upgraded",
thingName, coiotVers, iVersion);
thingHandler.reinitializeThing();
coiotBound = false;
}
if (!coiotBound) {
thingHandler.updateProperties(PROPERTY_COAP_VERSION, sVersion);
logger.debug("{}: CoIoT Version {} detected", thingName, iVersion);
if (iVersion == COIOT_VERSION_1) {
coiot = new Shelly1CoIoTVersion1(thingName, thingHandler, blkMap, sensorMap);
} else if (iVersion == COIOT_VERSION_2) {
coiot = new Shelly1CoIoTVersion2(thingName, thingHandler, blkMap, sensorMap);
} else {
logger.warn("{}: Unsupported CoAP version detected: {}", thingName, sVersion);
if (opt instanceof StringOption strOpt) {
devId = strOpt.getStringValue();
String sVersion = substringAfterLast(devId, "#");
int iVersion;
try {
iVersion = Integer.parseInt(sVersion);
} catch (NumberFormatException e) {
logger.debug("{}: Unable to parse version in CoIoT message: {}", thingName, devId);
thingHandler.incProtErrors();
return;
}
coiotVers = iVersion;
coiotBound = true;
if (coiotBound && coiotVers != iVersion) {
logger.debug("{}: CoIoT version has changed from {} to {}, maybe the firmware was upgraded",
thingName, coiotVers, iVersion);
thingHandler.reinitializeThing();
coiotBound = false;
}
if (!coiotBound) {
thingHandler.updateProperties(PROPERTY_COAP_VERSION, sVersion);
logger.debug("{}: CoIoT Version {} detected", thingName, iVersion);
if (iVersion == COIOT_VERSION_1) {
coiot = new Shelly1CoIoTVersion1(thingName, thingHandler, blkMap, sensorMap);
} else if (iVersion == COIOT_VERSION_2) {
coiot = new Shelly1CoIoTVersion2(thingName, thingHandler, blkMap, sensorMap);
} else {
logger.warn("{}: Unsupported CoAP version detected: {}", thingName, sVersion);
return;
}
coiotVers = iVersion;
coiotBound = true;
}
} else {
logger.debug("{}: COIOT_OPTION_GLOBAL_DEVID option is not a StringOption, type: {}", thingName,
opt.getClass().getSimpleName());
}
break;
case COIOT_OPTION_STATUS_VALIDITY:
break;
case COIOT_OPTION_STATUS_SERIAL:
serial = opt.getIntegerValue();
if (opt instanceof IntegerOption intOpt) {
serial = intOpt.getIntegerValue();
} else {
logger.debug("{}: COIOT_OPTION_STATUS_SERIAL option is not an IntegerOption, type: {}",
thingName, opt.getClass().getSimpleName());
}
break;
default:
logger.debug("{} ({}): CoAP option {} with value {} skipped", thingName, devId, opt.getNumber(),
opt.getValue());
opt.toValueString());
}
}
@@ -12,6 +12,9 @@
*/
package org.openhab.binding.shelly.internal.api1;
import static org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO.COIOT_OPTION_GLOBAL_DEVID;
import static org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO.COIOT_OPTION_STATUS_SERIAL;
import static org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO.COIOT_OPTION_STATUS_VALIDITY;
import static org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO.COIOT_PORT;
import java.net.InetAddress;
@@ -27,6 +30,11 @@ import org.eclipse.californium.core.coap.CoAP.Code;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.coap.option.IntegerOption;
import org.eclipse.californium.core.coap.option.MapBasedOptionRegistry;
import org.eclipse.californium.core.coap.option.OptionRegistry;
import org.eclipse.californium.core.coap.option.StandardOptionRegistry;
import org.eclipse.californium.core.coap.option.StringOption;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.elements.UdpMulticastConnector;
@@ -44,9 +52,27 @@ import org.slf4j.LoggerFactory;
*/
@NonNullByDefault
public class Shelly1CoapServer {
// Define custom CoIoT option definitions for Shelly devices
private static final StringOption.Definition COIOT_GLOBAL_DEVID_DEF = new StringOption.Definition(
COIOT_OPTION_GLOBAL_DEVID, "Shelly-GlobalDevId", true, 0, 255);
private static final IntegerOption.Definition COIOT_STATUS_VALIDITY_DEF = new IntegerOption.Definition(
COIOT_OPTION_STATUS_VALIDITY, "Shelly-StatusValidity", true, 0, 4);
private static final IntegerOption.Definition COIOT_STATUS_SERIAL_DEF = new IntegerOption.Definition(
COIOT_OPTION_STATUS_SERIAL, "Shelly-StatusSerial", true, 0, 4);
static {
// register configurations before Configuration.getStandard() is used
DtlsConfig.register();
// register custom CoIoT options for Shelly devices
// Merge with existing default registry to preserve options from other add-ons (e.g., Tradfri)
OptionRegistry currentRegistry = StandardOptionRegistry.getDefaultOptionRegistry();
OptionRegistry mergedRegistry = MapBasedOptionRegistry.builder() //
.add(currentRegistry) //
.add(COIOT_GLOBAL_DEVID_DEF) //
.add(COIOT_STATUS_VALIDITY_DEF) //
.add(COIOT_STATUS_SERIAL_DEF) //
.build();
StandardOptionRegistry.setDefaultOptionRegistry(mergedRegistry);
}
private final Logger logger = LoggerFactory.getLogger(Shelly1CoapServer.class);