[shelly] Fix Gen2 auth, improved security for Gen1 auth, improved discovery (#15898)

Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
Markus Michels 2023-11-18 16:28:44 +01:00 committed by GitHub
parent 608007c67e
commit 45786fa12c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 164 additions and 131 deletions

View File

@ -118,6 +118,10 @@ public class ShellyApiException extends Exception {
|| exType == NoRouteToHostException.class; || exType == NoRouteToHostException.class;
} }
public boolean isNoRouteToHost() {
return getCauseClass() == NoRouteToHostException.class;
}
public boolean isUnknownHost() { public boolean isUnknownHost() {
return getCauseClass() == UnknownHostException.class; return getCauseClass() == UnknownHostException.class;
} }

View File

@ -15,6 +15,7 @@ package org.openhab.binding.shelly.internal.api;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
@ -42,7 +43,8 @@ public interface ShellyApiInterface {
ShellySettingsDevice getDeviceInfo() throws ShellyApiException; ShellySettingsDevice getDeviceInfo() throws ShellyApiException;
ShellyDeviceProfile getDeviceProfile(String thingType) throws ShellyApiException; ShellyDeviceProfile getDeviceProfile(String thingType, @Nullable ShellySettingsDevice device)
throws ShellyApiException;
ShellySettingsStatus getStatus() throws ShellyApiException; ShellySettingsStatus getStatus() throws ShellyApiException;

View File

@ -24,6 +24,7 @@ import java.util.regex.Pattern;
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.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsGlobal; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsGlobal;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsInput; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsInput;
@ -47,24 +48,21 @@ import com.google.gson.Gson;
@NonNullByDefault @NonNullByDefault
public class ShellyDeviceProfile { public class ShellyDeviceProfile {
private final Logger logger = LoggerFactory.getLogger(ShellyDeviceProfile.class); private final Logger logger = LoggerFactory.getLogger(ShellyDeviceProfile.class);
private static final Pattern VERSION_PATTERN = Pattern.compile("v\\d+\\.\\d+\\.\\d+(-[a-z0-9]*)?"); private static final Pattern GEN1_VERSION_PATTERN = Pattern.compile("v\\d+\\.\\d+\\.\\d+(-[a-z0-9]*)?");
private static final Pattern GEN2_VERSION_PATTERN = Pattern.compile("\\d+\\.\\d+\\.\\d+(-[a-fh-z0-9]*)?");
public boolean initialized = false; // true when initialized public boolean initialized = false; // true when initialized
public String thingName = ""; public String thingName = "";
public String deviceType = "";
public boolean extFeatures = false; public boolean extFeatures = false;
public String settingsJson = ""; public String settingsJson = "";
public ShellySettingsDevice device = new ShellySettingsDevice();
public ShellySettingsGlobal settings = new ShellySettingsGlobal(); public ShellySettingsGlobal settings = new ShellySettingsGlobal();
public ShellySettingsStatus status = new ShellySettingsStatus(); public ShellySettingsStatus status = new ShellySettingsStatus();
public String hostname = "";
public String name = ""; public String name = "";
public String model = "";
public String mode = "";
public boolean discoverable = true; public boolean discoverable = true;
public boolean auth = false;
public boolean alwaysOn = true; public boolean alwaysOn = true;
public boolean isGen2 = false; public boolean isGen2 = false;
public boolean isBlu = false; public boolean isBlu = false;
@ -72,7 +70,6 @@ public class ShellyDeviceProfile {
public String hwRev = ""; public String hwRev = "";
public String hwBatchId = ""; public String hwBatchId = "";
public String mac = "";
public String fwVersion = ""; public String fwVersion = "";
public String fwDate = ""; public String fwDate = "";
@ -118,10 +115,13 @@ public class ShellyDeviceProfile {
public ShellyDeviceProfile() { public ShellyDeviceProfile() {
} }
public ShellyDeviceProfile initialize(String thingType, String jsonIn) throws ShellyApiException { public ShellyDeviceProfile initialize(String thingType, String jsonIn, @Nullable ShellySettingsDevice device)
throws ShellyApiException {
Gson gson = new Gson(); Gson gson = new Gson();
initialized = false; initialized = false;
if (device != null) {
this.device = device;
}
initFromThingType(thingType); initFromThingType(thingType);
@ -141,36 +141,36 @@ public class ShellyDeviceProfile {
settings = fromJson(gson, json, ShellySettingsGlobal.class); settings = fromJson(gson, json, ShellySettingsGlobal.class);
// General settings // General settings
if (getString(device.hostname).isEmpty() && !getString(device.mac).isEmpty()) {
device.hostname = device.mac.length() >= 12 ? "shelly-" + device.mac.toUpperCase().substring(6, 11)
: "unknown";
}
name = getString(settings.name); name = getString(settings.name);
deviceType = getString(settings.device.type);
mac = getString(settings.device.mac);
hostname = !getString(settings.device.hostname).isEmpty() ? settings.device.hostname.toLowerCase()
: mac.length() >= 12 ? "shelly-" + mac.toUpperCase().substring(6, 11) : "unknown";
mode = getString(settings.mode).toLowerCase();
hwRev = settings.hwinfo != null ? getString(settings.hwinfo.hwRevision) : ""; hwRev = settings.hwinfo != null ? getString(settings.hwinfo.hwRevision) : "";
hwBatchId = settings.hwinfo != null ? getString(settings.hwinfo.batchId.toString()) : ""; hwBatchId = settings.hwinfo != null ? getString(settings.hwinfo.batchId.toString()) : "";
fwDate = substringBefore(settings.fw, "/"); fwDate = substringBefore(device.fw, "-");
fwVersion = extractFwVersion(settings.fw); fwVersion = extractFwVersion(device.fw);
ShellyVersionDTO version = new ShellyVersionDTO(); ShellyVersionDTO version = new ShellyVersionDTO();
extFeatures = version.compare(fwVersion, SHELLY_API_FW_110) >= 0; extFeatures = version.compare(fwVersion, SHELLY_API_FW_110) >= 0;
discoverable = (settings.discoverable == null) || settings.discoverable; discoverable = (settings.discoverable == null) || settings.discoverable;
String mode = getString(device.mode);
isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER); isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER);
inColor = isLight && mode.equalsIgnoreCase(SHELLY_MODE_COLOR); inColor = isLight && mode.equalsIgnoreCase(SHELLY_MODE_COLOR);
numRelays = !isLight ? getInteger(settings.device.numOutputs) : 0; numRelays = !isLight ? getInteger(device.numOutputs) : 0;
if ((numRelays > 0) && (settings.relays == null)) { if ((numRelays > 0) && (settings.relays == null)) {
numRelays = 0; numRelays = 0;
} }
hasRelays = (numRelays > 0) || isDimmer; hasRelays = (numRelays > 0) || isDimmer;
numRollers = getInteger(settings.device.numRollers); numRollers = getInteger(device.numRollers);
numInputs = settings.inputs != null ? settings.inputs.size() : hasRelays ? isRoller ? 2 : 1 : 0; numInputs = settings.inputs != null ? settings.inputs.size() : hasRelays ? isRoller ? 2 : 1 : 0;
isEMeter = settings.emeters != null; isEMeter = settings.emeters != null;
numMeters = !isEMeter ? getInteger(settings.device.numMeters) : getInteger(settings.device.numEMeters); numMeters = !isEMeter ? getInteger(device.numMeters) : getInteger(device.numEMeters);
if ((numMeters == 0) && isLight) { if ((numMeters == 0) && isLight) {
// RGBW2 doesn't report, but has one // RGBW2 doesn't report, but has one
numMeters = inColor ? 1 : getInteger(settings.device.numOutputs); numMeters = inColor ? 1 : getInteger(device.numOutputs);
} }
initialized = true; initialized = true;
@ -199,8 +199,9 @@ public class ShellyDeviceProfile {
isGen2 = isGeneration2(thingType); isGen2 = isGeneration2(thingType);
isBlu = isBluSeries(thingType); // e.g. SBBT for BLU Button isBlu = isBluSeries(thingType); // e.g. SBBT for BLU Button
isDimmer = deviceType.equalsIgnoreCase(SHELLYDT_DIMMER) || deviceType.equalsIgnoreCase(SHELLYDT_DIMMER2) String type = getString(device.type);
|| deviceType.equalsIgnoreCase(SHELLYDT_PLUSDIMMERUS) isDimmer = type.equalsIgnoreCase(SHELLYDT_DIMMER) || type.equalsIgnoreCase(SHELLYDT_DIMMER2)
|| type.equalsIgnoreCase(SHELLYDT_PLUSDIMMERUS)
|| thingType.equalsIgnoreCase(THING_TYPE_SHELLYPLUSDIMMERUS_STR); || thingType.equalsIgnoreCase(THING_TYPE_SHELLYPLUSDIMMERUS_STR);
isBulb = thingType.equals(THING_TYPE_SHELLYBULB_STR); isBulb = thingType.equals(THING_TYPE_SHELLYBULB_STR);
isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR) isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)
@ -390,7 +391,8 @@ public class ShellyDeviceProfile {
.replace("/v1.12-", "/v1.12.0"); .replace("/v1.12-", "/v1.12.0");
// Extract version from string, e.g. 20210226-091047/v1.10.0-rc2-89-g623b41ec0-master // Extract version from string, e.g. 20210226-091047/v1.10.0-rc2-89-g623b41ec0-master
Matcher matcher = VERSION_PATTERN.matcher(vers); Matcher matcher = version.startsWith("v") ? GEN1_VERSION_PATTERN.matcher(vers)
: GEN2_VERSION_PATTERN.matcher(vers);
if (matcher.find()) { if (matcher.find()) {
return matcher.group(0); return matcher.group(0);
} }

View File

@ -69,6 +69,7 @@ public class ShellyHttpClient {
protected int timeoutErrors = 0; protected int timeoutErrors = 0;
protected int timeoutsRecovered = 0; protected int timeoutsRecovered = 0;
private ShellyDeviceProfile profile; private ShellyDeviceProfile profile;
protected boolean basicAuth = false;
public ShellyHttpClient(String thingName, ShellyThingInterface thing) { public ShellyHttpClient(String thingName, ShellyThingInterface thing) {
this(thingName, thing.getThingConfig(), thing.getHttpClient()); this(thingName, thing.getThingConfig(), thing.getHttpClient());
@ -83,9 +84,6 @@ public class ShellyHttpClient {
this.httpClient.setConnectTimeout(SHELLY_API_TIMEOUT_MS); this.httpClient.setConnectTimeout(SHELLY_API_TIMEOUT_MS);
} }
public void initialize() throws ShellyApiException {
}
public void setConfig(String thingName, ShellyThingConfiguration config) { public void setConfig(String thingName, ShellyThingConfiguration config) {
this.thingName = thingName; this.thingName = thingName;
this.config = config; this.config = config;
@ -167,7 +165,7 @@ public class ShellyHttpClient {
authHeader = formatAuthResponse(uri, authHeader = formatAuthResponse(uri,
buildAuthResponse(uri, auth, SHELLY2_AUTHDEF_USER, config.password)); buildAuthResponse(uri, auth, SHELLY2_AUTHDEF_USER, config.password));
} else { } else {
if (!uri.equals(SHELLYRPC_ENDPOINT)) { if (basicAuth) {
String bearer = config.userId + ":" + config.password; String bearer = config.userId + ":" + config.password;
authHeader = HTTP_AUTH_TYPE_BASIC + " " + Base64.getEncoder().encodeToString(bearer.getBytes()); authHeader = HTTP_AUTH_TYPE_BASIC + " " + Base64.getEncoder().encodeToString(bearer.getBytes());
} }

View File

@ -261,6 +261,10 @@ public class Shelly1ApiJsonDTO {
public static class ShellySettingsDevice { public static class ShellySettingsDevice {
public String type; public String type;
public String mode; // Gen 1
public String id; // Gen2: service name
public String name; // Gen2: configured device name
public String profile; // Gen 2
public String mac; public String mac;
public String hostname; public String hostname;
public String fw; public String fw;
@ -563,7 +567,6 @@ public class Shelly1ApiJsonDTO {
public static class ShellySettingsGlobal { public static class ShellySettingsGlobal {
// https://shelly-api-docs.shelly.cloud/#shelly1pm-settings // https://shelly-api-docs.shelly.cloud/#shelly1pm-settings
public ShellySettingsDevice device = new ShellySettingsDevice();
@SerializedName("wifi_ap") @SerializedName("wifi_ap")
public ShellySettingsWiFiAp wifiAp = new ShellySettingsWiFiAp(); public ShellySettingsWiFiAp wifiAp = new ShellySettingsWiFiAp();
@SerializedName("wifi_sta") @SerializedName("wifi_sta")

View File

@ -182,10 +182,10 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
for (Option opt : options) { for (Option opt : options) {
if (opt.getNumber() == COIOT_OPTION_GLOBAL_DEVID) { if (opt.getNumber() == COIOT_OPTION_GLOBAL_DEVID) {
String devid = opt.getStringValue(); String devid = opt.getStringValue();
if (devid.contains("#") && profile.mac != null) { if (devid.contains("#") && profile.device.mac != null) {
// Format: <device type>#<mac address>#<coap version> // Format: <device type>#<mac address>#<coap version>
String macid = substringBetween(devid, "#", "#"); String macid = substringBetween(devid, "#", "#");
if (profile.mac.toUpperCase().contains(macid.toUpperCase())) { if (getString(profile.device.mac).toUpperCase().contains(macid.toUpperCase())) {
match = true; match = true;
break; break;
} }

View File

@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.shelly.internal.api.ShellyApiException; import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api.ShellyApiInterface; import org.openhab.binding.shelly.internal.api.ShellyApiInterface;
@ -79,10 +80,26 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
this.profile = new ShellyDeviceProfile(); this.profile = new ShellyDeviceProfile();
} }
@Override
public void initialize() throws ShellyApiException {
profile.device = getDeviceInfo();
}
@Override @Override
public ShellySettingsDevice getDeviceInfo() throws ShellyApiException { public ShellySettingsDevice getDeviceInfo() throws ShellyApiException {
ShellySettingsDevice info = callApi(SHELLY_URL_DEVINFO, ShellySettingsDevice.class); ShellySettingsDevice info = callApi(SHELLY_URL_DEVINFO, ShellySettingsDevice.class);
info.gen = 1; info.gen = 1;
basicAuth = getBool(info.auth);
if (getString(info.mode).isEmpty()) { // older Gen1 Firmware
if (getInteger(info.numRollers) > 0) {
info.mode = SHELLY_CLASS_ROLLER;
} else if (getInteger(info.numOutputs) > 0) {
info.mode = SHELLY_CLASS_RELAY;
} else {
info.mode = "";
}
}
return info; return info;
} }
@ -104,7 +121,14 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
* @throws ShellyApiException * @throws ShellyApiException
*/ */
@Override @Override
public ShellyDeviceProfile getDeviceProfile(String thingType) throws ShellyApiException { public ShellyDeviceProfile getDeviceProfile(String thingType, @Nullable ShellySettingsDevice device)
throws ShellyApiException {
if (device != null) {
profile.device = device;
}
if (profile.device.type == null) {
profile.device = getDeviceInfo();
}
String json = httpRequest(SHELLY_URL_SETTINGS); String json = httpRequest(SHELLY_URL_SETTINGS);
if (json.contains("\"type\":\"SHDM-")) { if (json.contains("\"type\":\"SHDM-")) {
logger.trace("{}: Detected a Shelly Dimmer: fix Json (replace lights[] tag with dimmers[]", thingName); logger.trace("{}: Detected a Shelly Dimmer: fix Json (replace lights[] tag with dimmers[]", thingName);
@ -112,10 +136,10 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
} }
// Map settings to device profile for Light and Sense // Map settings to device profile for Light and Sense
profile.initialize(thingType, json); profile.initialize(thingType, json, profile.device);
// 2nd level initialization // 2nd level initialization
profile.thingName = profile.hostname; profile.thingName = profile.device.hostname;
if (profile.isLight && (profile.numMeters == 0)) { if (profile.isLight && (profile.numMeters == 0)) {
logger.debug("{}: Get number of meters from light status", thingName); logger.debug("{}: Get number of meters from light status", thingName);
ShellyStatusLight status = getLightStatus(); ShellyStatusLight status = getLightStatus();
@ -396,10 +420,10 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
*/ */
@Override @Override
public void setLightMode(String mode) throws ShellyApiException { public void setLightMode(String mode) throws ShellyApiException {
if (!mode.isEmpty() && !profile.mode.equals(mode)) { if (!mode.isEmpty() && !profile.device.mode.equals(mode)) {
setLightSetting(SHELLY_API_MODE, mode); setLightSetting(SHELLY_API_MODE, mode);
profile.mode = mode; profile.device.mode = mode;
profile.inColor = profile.isLight && profile.mode.equalsIgnoreCase(SHELLY_MODE_COLOR); profile.inColor = profile.isLight && mode.equalsIgnoreCase(SHELLY_MODE_COLOR);
} }
} }

View File

@ -148,6 +148,11 @@ public class Shelly2ApiClient extends ShellyHttpClient {
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_STOPPED, SHELLY_RSTATE_STOP); MAP_ROLLER_STATE.put(SHELLY2_RSTATE_STOPPED, SHELLY_RSTATE_STOP);
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_CALIB, SHELLY2_RSTATE_CALIB); // Gen2-only MAP_ROLLER_STATE.put(SHELLY2_RSTATE_CALIB, SHELLY2_RSTATE_CALIB); // Gen2-only
} }
protected static final Map<String, String> MAP_PROFILE = new HashMap<>();
static {
MAP_PROFILE.put(SHELLY_CLASS_RELAY, SHELLY2_PROFILE_RELAY);
MAP_PROFILE.put(SHELLY_CLASS_ROLLER, SHELLY2_PROFILE_COVER);
}
protected @Nullable ArrayList<@Nullable ShellySettingsRelay> fillRelaySettings(ShellyDeviceProfile profile, protected @Nullable ArrayList<@Nullable ShellySettingsRelay> fillRelaySettings(ShellyDeviceProfile profile,
Shelly2GetConfigResult dc) { Shelly2GetConfigResult dc) {

View File

@ -82,7 +82,7 @@ public class Shelly2ApiJsonDTO {
// Component types // Component types
public static final String SHELLY2_PROFILE_RELAY = "switch"; public static final String SHELLY2_PROFILE_RELAY = "switch";
public static final String SHELLY2_PROFILE_ROLLER = "cover"; public static final String SHELLY2_PROFILE_COVER = "cover";
// Button types/modes // Button types/modes
public static final String SHELLY2_BTNT_MOMENTARY = "momentary"; public static final String SHELLY2_BTNT_MOMENTARY = "momentary";
@ -183,13 +183,14 @@ public class Shelly2ApiJsonDTO {
public String id; public String id;
public String mac; public String mac;
public String model; public String model;
public String profile;
public Integer gen; public Integer gen;
@SerializedName("fw_id") @SerializedName("fw_id")
public String firmware; public String fw;
public String ver; public String ver;
public String app; public String app;
@SerializedName("auth_en") @SerializedName("auth_en")
public Boolean authEnable; public Boolean auth;
@SerializedName("auth_domain") @SerializedName("auth_domain")
public String authDomain; public String authDomain;
} }

View File

@ -113,11 +113,6 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
this.thingName = thingName; this.thingName = thingName;
this.thing = thing; this.thing = thing;
this.thingTable = thingTable; this.thingTable = thingTable;
try {
getProfile().initFromThingType(thing.getThingType());
} catch (ShellyApiException e) {
logger.info("{}: Shelly2 API initialization failed!", thingName, e);
}
} }
/** /**
@ -161,9 +156,17 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
@SuppressWarnings("null") @SuppressWarnings("null")
@Override @Override
public ShellyDeviceProfile getDeviceProfile(String thingType) throws ShellyApiException { public ShellyDeviceProfile getDeviceProfile(String thingType, @Nullable ShellySettingsDevice devInfo)
throws ShellyApiException {
ShellyDeviceProfile profile = thing != null ? getProfile() : new ShellyDeviceProfile(); ShellyDeviceProfile profile = thing != null ? getProfile() : new ShellyDeviceProfile();
if (devInfo != null) {
profile.device = devInfo;
}
if (profile.device.type == null) {
profile.device = getDeviceInfo();
}
Shelly2GetConfigResult dc = apiRequest(SHELLYRPC_METHOD_GETCONFIG, null, Shelly2GetConfigResult.class); Shelly2GetConfigResult dc = apiRequest(SHELLYRPC_METHOD_GETCONFIG, null, Shelly2GetConfigResult.class);
profile.isGen2 = true; profile.isGen2 = true;
profile.settingsJson = gson.toJson(dc); profile.settingsJson = gson.toJson(dc);
@ -195,29 +198,18 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
profile.numRelays = profile.settings.relays != null ? profile.settings.relays.size() : 0; profile.numRelays = profile.settings.relays != null ? profile.settings.relays.size() : 0;
profile.numRollers = profile.settings.rollers != null ? profile.settings.rollers.size() : 0; profile.numRollers = profile.settings.rollers != null ? profile.settings.rollers.size() : 0;
profile.hasRelays = profile.numRelays > 0 || profile.numRollers > 0; profile.hasRelays = profile.numRelays > 0 || profile.numRollers > 0;
profile.mode = ""; if (getString(profile.device.mode).isEmpty() && profile.hasRelays) {
if (profile.hasRelays) { profile.device.mode = profile.isRoller ? SHELLY_CLASS_ROLLER : SHELLY_CLASS_RELAY;
profile.mode = profile.isRoller ? SHELLY_CLASS_ROLLER : SHELLY_CLASS_RELAY;
} }
ShellySettingsDevice device = getDeviceInfo(); ShellySettingsDevice device = profile.device;
profile.settings.device = device;
if (!getString(device.fw).isEmpty()) {
profile.fwDate = substringBefore(device.fw, "/");
profile.fwVersion = profile.status.update.oldVersion = "v" + substringAfter(device.fw, "/");
}
profile.hostname = device.hostname;
profile.deviceType = device.type;
profile.mac = device.mac;
profile.auth = device.auth;
profile.isGen2 = device.gen == 2; profile.isGen2 = device.gen == 2;
if (config.serviceName.isEmpty()) { if (config.serviceName.isEmpty()) {
config.serviceName = getString(profile.hostname); config.serviceName = getString(profile.device.hostname);
} }
profile.fwDate = substringBefore(device.fw, "/"); profile.settings.fw = device.fw;
profile.fwVersion = substringBefore(ShellyDeviceProfile.extractFwVersion(device.fw.replace("/", "/v")), "-"); profile.fwDate = substringBefore(substringBefore(device.fw, "/"), "-");
profile.status.update.oldVersion = profile.fwVersion; profile.fwVersion = profile.status.update.oldVersion = ShellyDeviceProfile.extractFwVersion(device.fw);
profile.status.hasUpdate = profile.status.update.hasUpdate = false; profile.status.hasUpdate = profile.status.update.hasUpdate = false;
if (dc.eth != null) { if (dc.eth != null) {
@ -741,11 +733,13 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
Shelly2DeviceSettings device = callApi("/shelly", Shelly2DeviceSettings.class); Shelly2DeviceSettings device = callApi("/shelly", Shelly2DeviceSettings.class);
ShellySettingsDevice info = new ShellySettingsDevice(); ShellySettingsDevice info = new ShellySettingsDevice();
info.hostname = getString(device.id); info.hostname = getString(device.id);
info.fw = getString(device.firmware); info.name = getString(device.name);
info.fw = getString(device.fw);
info.type = getString(device.model); info.type = getString(device.model);
info.mac = getString(device.mac); info.mac = getString(device.mac);
info.auth = getBool(device.authEnable); info.auth = getBool(device.auth);
info.gen = getInteger(device.gen); info.gen = getInteger(device.gen);
info.mode = mapValue(MAP_PROFILE, device.profile);
return info; return info;
} }
@ -770,16 +764,16 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
profile.settings.sleepMode.period = ds.sys.wakeupPeriod / 60; profile.settings.sleepMode.period = ds.sys.wakeupPeriod / 60;
} }
status.hasUpdate = status.update.hasUpdate = false;
status.update.oldVersion = getProfile().fwVersion;
if (ds.sys.availableUpdates != null) { if (ds.sys.availableUpdates != null) {
status.update.hasUpdate = ds.sys.availableUpdates.stable != null; status.update.hasUpdate = ds.sys.availableUpdates.stable != null;
if (ds.sys.availableUpdates.stable != null) { if (ds.sys.availableUpdates.stable != null) {
status.update.newVersion = "v" + getString(ds.sys.availableUpdates.stable.version); status.update.newVersion = ShellyDeviceProfile
.extractFwVersion(getString(ds.sys.availableUpdates.stable.version));
status.hasUpdate = new ShellyVersionDTO().compare(profile.fwVersion, status.update.newVersion) < 0; status.hasUpdate = new ShellyVersionDTO().compare(profile.fwVersion, status.update.newVersion) < 0;
} }
if (ds.sys.availableUpdates.beta != null) { if (ds.sys.availableUpdates.beta != null) {
status.update.betaVersion = "v" + getString(ds.sys.availableUpdates.beta.version); status.update.betaVersion = ShellyDeviceProfile
.extractFwVersion(getString(ds.sys.availableUpdates.beta.version));
status.hasUpdate = new ShellyVersionDTO().compare(profile.fwVersion, status.update.betaVersion) < 0; status.hasUpdate = new ShellyVersionDTO().compare(profile.fwVersion, status.update.betaVersion) < 0;
} }
} }

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api.ShellyApiException; import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile; import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyInputState; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyInputState;
@ -119,9 +120,13 @@ public class ShellyBluApi extends Shelly2ApiRpc {
} }
@Override @Override
public ShellyDeviceProfile getDeviceProfile(String thingType) throws ShellyApiException { public ShellyDeviceProfile getDeviceProfile(String thingType, @Nullable ShellySettingsDevice devInfo)
throws ShellyApiException {
ShellyDeviceProfile profile = thing != null ? getProfile() : new ShellyDeviceProfile(); ShellyDeviceProfile profile = thing != null ? getProfile() : new ShellyDeviceProfile();
if (devInfo != null) {
profile.device = devInfo;
}
profile.isBlu = true; profile.isBlu = true;
profile.settingsJson = "{}"; profile.settingsJson = "{}";
profile.thingName = thingName; profile.thingName = thingName;
@ -131,13 +136,8 @@ public class ShellyBluApi extends Shelly2ApiRpc {
} }
ShellySettingsDevice device = getDeviceInfo(); ShellySettingsDevice device = getDeviceInfo();
profile.settings.device = device;
profile.hostname = device.hostname;
profile.deviceType = device.type;
profile.mac = device.mac;
profile.auth = device.auth;
if (config.serviceName.isEmpty()) { if (config.serviceName.isEmpty()) {
config.serviceName = getString(profile.hostname); config.serviceName = getString(profile.device.hostname);
} }
profile.fwDate = substringBefore(device.fw, "/"); profile.fwDate = substringBefore(device.fw, "/");
profile.fwVersion = substringBefore(ShellyDeviceProfile.extractFwVersion(device.fw.replace("/", "/v")), "-"); profile.fwVersion = substringBefore(ShellyDeviceProfile.extractFwVersion(device.fw.replace("/", "/v")), "-");

View File

@ -31,6 +31,7 @@ import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api.ShellyApiInterface; import org.openhab.binding.shelly.internal.api.ShellyApiInterface;
import org.openhab.binding.shelly.internal.api.ShellyApiResult; import org.openhab.binding.shelly.internal.api.ShellyApiResult;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile; import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
import org.openhab.binding.shelly.internal.api1.Shelly1HttpApi; import org.openhab.binding.shelly.internal.api1.Shelly1HttpApi;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiRpc; import org.openhab.binding.shelly.internal.api2.Shelly2ApiRpc;
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration; import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
@ -144,17 +145,23 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
boolean gen2 = "2".equals(service.getPropertyString("gen")); boolean gen2 = "2".equals(service.getPropertyString("gen"));
ShellyApiInterface api = null; ShellyApiInterface api = null;
ShellySettingsDevice devInfo;
try { try {
api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient); api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient);
api.initialize(); api.initialize();
profile = api.getDeviceProfile(thingType); devInfo = api.getDeviceInfo();
model = devInfo.type;
if (devInfo.name != null) {
deviceName = devInfo.name;
}
profile = api.getDeviceProfile(thingType, devInfo);
api.close();
logger.debug("{}: Shelly settings : {}", name, profile.settingsJson); logger.debug("{}: Shelly settings : {}", name, profile.settingsJson);
deviceName = profile.name; deviceName = profile.name;
model = profile.deviceType; mode = devInfo.mode;
mode = profile.mode;
properties = ShellyBaseHandler.fillDeviceProperties(profile); properties = ShellyBaseHandler.fillDeviceProperties(profile);
logger.trace("{}: thingType={}, deviceType={}, mode={}, symbolic name={}", name, thingType, logger.trace("{}: thingType={}, deviceType={}, mode={}, symbolic name={}", name, thingType,
profile.deviceType, mode.isEmpty() ? "<standard>" : mode, deviceName); devInfo.type, mode.isEmpty() ? "<standard>" : mode, deviceName);
// get thing type from device name // get thing type from device name
thingUID = ShellyThingCreator.getThingUID(name, model, mode, false); thingUID = ShellyThingCreator.getThingUID(name, model, mode, false);

View File

@ -273,45 +273,41 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING, updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
messages.get("status.unknown.initializing")); messages.get("status.unknown.initializing"));
profile.initFromThingType(thingType); // do some basic initialization
// Gen 1 only: Setup CoAP listener to we get the CoAP message, which triggers initialization even the thing // Gen 1 only: Setup CoAP listener to we get the CoAP message, which triggers initialization even the thing
// could not be fully initialized here. In this case the CoAP messages triggers auto-initialization (like the // could not be fully initialized here. In this case the CoAP messages triggers auto-initialization (like the
// Action URL does when enabled) // Action URL does when enabled)
profile.initFromThingType(thingType);
if (coap != null && config.eventsCoIoT && !profile.alwaysOn) { if (coap != null && config.eventsCoIoT && !profile.alwaysOn) {
coap.start(thingName, config); coap.start(thingName, config);
} }
// Initialize API access, exceptions will be catched by initialize() // Initialize API access, exceptions will be catched by initialize()
api.initialize(); api.initialize();
ShellySettingsDevice devInfo = api.getDeviceInfo(); ShellySettingsDevice device = profile.device = api.getDeviceInfo();
if (getBool(devInfo.auth) && config.password.isEmpty()) { if (getBool(device.auth) && config.password.isEmpty()) {
setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-no-credentials"); setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-no-credentials");
return false; return false;
} }
if (config.serviceName.isEmpty()) { if (config.serviceName.isEmpty()) {
config.serviceName = getString(profile.hostname).toLowerCase(); config.serviceName = getString(device.hostname).toLowerCase();
} }
api.setConfig(thingName, config); api.setConfig(thingName, config);
ShellyDeviceProfile tmpPrf = api.getDeviceProfile(thingType); ShellyDeviceProfile tmpPrf = api.getDeviceProfile(thingType, profile.device);
tmpPrf.isGen2 = gen2; String mode = getString(tmpPrf.device.mode);
tmpPrf.auth = devInfo.auth; // missing in /settings
if (this.getThing().getThingTypeUID().equals(THING_TYPE_SHELLYPROTECTED)) { if (this.getThing().getThingTypeUID().equals(THING_TYPE_SHELLYPROTECTED)) {
changeThingType(thingName, tmpPrf.mode); changeThingType(thingName, mode);
return false; // force re-initialization return false; // force re-initialization
} }
// Validate device mode // Validate device mode
String reqMode = thingType.contains("-") ? substringAfter(thingType, "-") : ""; String reqMode = thingType.contains("-") ? substringAfter(thingType, "-") : "";
if (!reqMode.isEmpty() && !tmpPrf.mode.equals(reqMode)) { if (!reqMode.isEmpty() && !mode.equals(reqMode)) {
setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-wrong-mode", tmpPrf.mode, setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-wrong-mode", mode, reqMode);
reqMode);
return false; return false;
} }
if (!getString(devInfo.coiot).isEmpty()) { if (!getString(tmpPrf.device.coiot).isEmpty()) {
// New Shelly devices might use a different endpoint for the CoAP listener // New Shelly devices might use a different endpoint for the CoAP listener
tmpPrf.coiotEndpoint = devInfo.coiot; tmpPrf.coiotEndpoint = tmpPrf.device.coiot;
} }
if (tmpPrf.settings.sleepMode != null && !tmpPrf.isTRV) { if (tmpPrf.settings.sleepMode != null && !tmpPrf.isTRV) {
// Sensor, usually 12h, H&T in USB mode 10min // Sensor, usually 12h, H&T in USB mode 10min
@ -566,13 +562,10 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
status = "offline.conf-error-access-denied"; status = "offline.conf-error-access-denied";
} else if (isWatchdogStarted()) { } else if (isWatchdogStarted()) {
if (!isWatchdogExpired()) { if (!isWatchdogExpired()) {
logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
if (profile.alwaysOn) { // suppress for battery powered sensors if (profile.alwaysOn) { // suppress for battery powered sensors
logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url); logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
} }
} else {
if (isThingOnline()) {
status = "offline.status-error-watchdog";
}
} }
} else if (e.isJSONException()) { } else if (e.isJSONException()) {
status = "offline.status-error-unexpected-api-result"; status = "offline.status-error-unexpected-api-result";
@ -606,22 +599,18 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
private void showThingConfig(ShellyDeviceProfile profile) { private void showThingConfig(ShellyDeviceProfile profile) {
logger.debug("{}: Initializing device {}, type {}, Hardware: Rev: {}, batch {}; Firmware: {} / {}", thingName, logger.debug("{}: Initializing device {}, type {}, Hardware: Rev: {}, batch {}; Firmware: {} / {}", thingName,
profile.hostname, profile.deviceType, profile.hwRev, profile.hwBatchId, profile.fwVersion, profile.device.hostname, profile.device.type, profile.hwRev, profile.hwBatchId, profile.fwVersion,
profile.fwDate); profile.fwDate);
logger.debug("{}: Shelly settings info for {}: {}", thingName, profile.hostname, profile.settingsJson); logger.debug("{}: Shelly settings info for {}: {}", thingName, profile.device.hostname, profile.settingsJson);
logger.debug( logger.debug("{}: Device "
""" + "hasRelays:{} (numRelays={}),isRoller:{} (numRoller={}),isDimmer:{},numMeter={},isEMeter:{}), ext. Switch Add-On: {}"
{}: Device \ + ",isSensor:{},isDS:{},hasBattery:{}{},isSense:{},isMotion:{},isLight:{},isBulb:{},isDuo:{},isRGBW2:{},inColor:{}, BLU Gateway support: {}"
hasRelays:{} (numRelays={}),isRoller:{} (numRoller={}),isDimmer:{},numMeter={},isEMeter:{}), ext. Switch Add-On: {}\ + ",alwaysOn:{}, updatePeriod:{}sec", thingName, profile.hasRelays, profile.numRelays, profile.isRoller,
,isSensor:{},isDS:{},hasBattery:{}{},isSense:{},isMotion:{},isLight:{},isBulb:{},isDuo:{},isRGBW2:{},inColor:{}, BLU Gateway support: {}\ profile.numRollers, profile.isDimmer, profile.numMeters, profile.isEMeter,
,alwaysOn:{}, updatePeriod:{}sec\ profile.settings.extSwitch != null ? "installed" : "n/a", profile.isSensor, profile.isDW,
""", profile.hasBattery, profile.hasBattery ? " (low battery threshold=" + config.lowBattery + "%)" : "",
thingName, profile.hasRelays, profile.numRelays, profile.isRoller, profile.numRollers, profile.isDimmer, profile.isSense, profile.isMotion, profile.isLight, profile.isBulb, profile.isDuo, profile.isRGBW2,
profile.numMeters, profile.isEMeter, profile.settings.extSwitch != null ? "installed" : "n/a", profile.inColor, profile.alwaysOn, profile.updatePeriod, config.enableBluGateway);
profile.isSensor, profile.isDW, profile.hasBattery,
profile.hasBattery ? " (low battery threshold=" + config.lowBattery + "%)" : "", profile.isSense,
profile.isMotion, profile.isLight, profile.isBulb, profile.isDuo, profile.isRGBW2, profile.inColor,
profile.alwaysOn, profile.updatePeriod, config.enableBluGateway);
if (profile.status.extTemperature != null || profile.status.extHumidity != null if (profile.status.extTemperature != null || profile.status.extHumidity != null
|| profile.status.extVoltage != null || profile.status.extAnalogInput != null) { || profile.status.extVoltage != null || profile.status.extAnalogInput != null) {
logger.debug("{}: Shelly Add-On detected with at least 1 external sensor", thingName); logger.debug("{}: Shelly Add-On detected with at least 1 external sensor", thingName);
@ -1059,15 +1048,15 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
// no fw version available (e.g. BLU device) // no fw version available (e.g. BLU device)
return; return;
} }
ShellyVersionDTO version = new ShellyVersionDTO(); ShellyVersionDTO version = new ShellyVersionDTO();
if (version.checkBeta(getString(prf.fwVersion))) { if (version.checkBeta(getString(prf.fwVersion))) {
logger.info("{}: {}", prf.hostname, messages.get("versioncheck.beta", prf.fwVersion, prf.fwDate)); logger.info("{}: {}", prf.device.hostname,
messages.get("versioncheck.beta", prf.fwVersion, prf.fwDate));
} else { } else {
String minVersion = !gen2 ? SHELLY_API_MIN_FWVERSION : SHELLY2_API_MIN_FWVERSION; String minVersion = !gen2 ? SHELLY_API_MIN_FWVERSION : SHELLY2_API_MIN_FWVERSION;
if (version.compare(prf.fwVersion, minVersion) < 0) { if (version.compare(prf.fwVersion, minVersion) < 0) {
logger.warn("{}: {}", prf.hostname, logger.warn("{}: {}", prf.device.hostname,
messages.get("versioncheck.tooold", prf.fwVersion, prf.fwDate, minVersion)); messages.get("versioncheck.beta", prf.fwVersion, prf.fwDate));
} }
} }
if (!gen2 && bindingConfig.autoCoIoT && ((version.compare(prf.fwVersion, SHELLY_API_MIN_FWCOIOT)) >= 0) if (!gen2 && bindingConfig.autoCoIoT && ((version.compare(prf.fwVersion, SHELLY_API_MIN_FWCOIOT)) >= 0)
@ -1450,10 +1439,10 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
Map<String, Object> properties = new TreeMap<>(); Map<String, Object> properties = new TreeMap<>();
properties.put(PROPERTY_VENDOR, VENDOR); properties.put(PROPERTY_VENDOR, VENDOR);
if (profile.isInitialized()) { if (profile.isInitialized()) {
properties.put(PROPERTY_MODEL_ID, getString(profile.settings.device.type)); properties.put(PROPERTY_MODEL_ID, getString(profile.device.type));
properties.put(PROPERTY_MAC_ADDRESS, profile.mac); properties.put(PROPERTY_MAC_ADDRESS, profile.device.mac);
properties.put(PROPERTY_FIRMWARE_VERSION, profile.fwVersion + "/" + profile.fwDate); properties.put(PROPERTY_FIRMWARE_VERSION, profile.fwVersion + "/" + profile.fwDate);
properties.put(PROPERTY_DEV_MODE, profile.mode); properties.put(PROPERTY_DEV_MODE, profile.device.mode);
if (profile.hasRelays) { if (profile.hasRelays) {
properties.put(PROPERTY_NUM_RELAYS, String.valueOf(profile.numRelays)); properties.put(PROPERTY_NUM_RELAYS, String.valueOf(profile.numRelays));
properties.put(PROPERTY_NUM_ROLLERS, String.valueOf(profile.numRollers)); properties.put(PROPERTY_NUM_ROLLERS, String.valueOf(profile.numRollers));
@ -1481,7 +1470,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
try { try {
refreshSettings |= forceRefresh; refreshSettings |= forceRefresh;
if (refreshSettings) { if (refreshSettings) {
profile = api.getDeviceProfile(thingType); profile = api.getDeviceProfile(thingType, null);
if (!isThingOnline()) { if (!isThingOnline()) {
logger.debug("{}: Device profile re-initialized (thingType={})", thingName, thingType); logger.debug("{}: Device profile re-initialized (thingType={})", thingName, thingType);
} }

View File

@ -82,7 +82,7 @@ public class ShellyLightHandler extends ShellyBaseHandler {
try { try {
ShellyColorUtils oldCol = getCurrentColors(lightId); ShellyColorUtils oldCol = getCurrentColors(lightId);
oldCol.mode = profile.mode; oldCol.mode = profile.device.mode;
ShellyColorUtils col = new ShellyColorUtils(oldCol); ShellyColorUtils col = new ShellyColorUtils(oldCol);
boolean update = true; boolean update = true;
@ -317,7 +317,7 @@ public class ShellyLightHandler extends ShellyBaseHandler {
} }
ShellyStatusLight status = api.getLightStatus(); ShellyStatusLight status = api.getLightStatus();
logger.trace("{}: Updating light status in {} mode, {} channel(s)", thingName, profile.mode, logger.trace("{}: Updating light status in {} mode, {} channel(s)", thingName, profile.device.mode,
status.lights.size()); status.lights.size());
// In white mode we have multiple channels // In white mode we have multiple channels

View File

@ -377,7 +377,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
list.put(ACTION_RES_STATS, "Reset Statistics"); list.put(ACTION_RES_STATS, "Reset Statistics");
list.put(ACTION_RESTART, "Reboot Device"); list.put(ACTION_RESTART, "Reboot Device");
if (gen2) { if (!gen2 || !profile.isBlu) {
list.put(ACTION_PROTECT, "Protect Device"); list.put(ACTION_PROTECT, "Protect Device");
} }

View File

@ -87,7 +87,7 @@ public class ShellyManagerOtaPage extends ShellyManagerPage {
String deviceType = getDeviceType(properties); String deviceType = getDeviceType(properties);
String uri = !url.isEmpty() && connection.equals(CONNECTION_TYPE_CUSTOM) ? url String uri = !url.isEmpty() && connection.equals(CONNECTION_TYPE_CUSTOM) ? url
: getFirmwareUrl(config.deviceIp, deviceType, profile.mode, version, : getFirmwareUrl(config.deviceIp, deviceType, profile.device.mode, version,
connection.equals(CONNECTION_TYPE_LOCAL)); connection.equals(CONNECTION_TYPE_LOCAL));
if (connection.equalsIgnoreCase(CONNECTION_TYPE_INTERNET)) { if (connection.equalsIgnoreCase(CONNECTION_TYPE_INTERNET)) {
// If target // If target
@ -100,7 +100,8 @@ public class ShellyManagerOtaPage extends ShellyManagerPage {
} }
} else if (connection.equalsIgnoreCase(CONNECTION_TYPE_LOCAL)) { } else if (connection.equalsIgnoreCase(CONNECTION_TYPE_LOCAL)) {
// redirect to local server -> http://<oh-ip>:<oh-port>/shelly/manager/ota?deviceType=xxx&version=xxx // redirect to local server -> http://<oh-ip>:<oh-port>/shelly/manager/ota?deviceType=xxx&version=xxx
String modeParm = !profile.mode.isEmpty() ? "&" + URLPARM_DEVMODE + "=" + profile.mode : ""; String modeParm = !profile.device.mode.isEmpty() ? "&" + URLPARM_DEVMODE + "=" + profile.device.mode
: "";
url = URLPARM_URL + "=http://" + localIp + ":" + localPort + SHELLY_MGR_OTA_URI + urlEncode( url = URLPARM_URL + "=http://" + localIp + ":" + localPort + SHELLY_MGR_OTA_URI + urlEncode(
"?" + URLPARM_DEVTYPE + "=" + deviceType + modeParm + "&" + URLPARM_VERSION + "=" + version); "?" + URLPARM_DEVTYPE + "=" + deviceType + modeParm + "&" + URLPARM_VERSION + "=" + version);
} else if (connection.equalsIgnoreCase(CONNECTION_TYPE_CUSTOM)) { } else if (connection.equalsIgnoreCase(CONNECTION_TYPE_CUSTOM)) {

View File

@ -143,7 +143,7 @@ public class ShellyManagerOverviewPage extends ShellyManagerPage {
try { try {
if (!profile.isGen2) { // currently there is no public firmware repo for Gen2 if (!profile.isGen2) { // currently there is no public firmware repo for Gen2
logger.debug("{}: Load firmware version list for device type {}", LOG_PREFIX, deviceType); logger.debug("{}: Load firmware version list for device type {}", LOG_PREFIX, deviceType);
FwRepoEntry fw = getFirmwareRepoEntry(deviceType, profile.mode); FwRepoEntry fw = getFirmwareRepoEntry(deviceType, profile.device.mode);
pVersion = extractFwVersion(fw.version); pVersion = extractFwVersion(fw.version);
bVersion = extractFwVersion(fw.betaVer); bVersion = extractFwVersion(fw.betaVer);
@ -238,7 +238,9 @@ public class ShellyManagerOverviewPage extends ShellyManagerPage {
// return handler.getChannelValue(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_UPDATE) == OnOffType.ON; // return handler.getChannelValue(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_UPDATE) == OnOffType.ON;
return getBool(profile.status.hasUpdate); return getBool(profile.status.hasUpdate);
case FILTER_UNPROTECTED: case FILTER_UNPROTECTED:
return !profile.auth; if (profile.device.auth != null) {
return !profile.device.auth;
}
case "*": case "*":
default: default:
return true; return true;

View File

@ -254,7 +254,8 @@ public class ShellyManagerPage {
properties.put(ATTRIBUTE_APR_TRESHOLD, properties.put(ATTRIBUTE_APR_TRESHOLD,
profile.settings.apRoaming != null ? getOption(profile.settings.apRoaming.threshold) : "n/a"); profile.settings.apRoaming != null ? getOption(profile.settings.apRoaming.threshold) : "n/a");
properties.put(ATTRIBUTE_PWD_PROTECT, properties.put(ATTRIBUTE_PWD_PROTECT,
profile.auth ? "enabled, user=" + getString(profile.settings.login.username) : "disabled"); getBool(profile.device.auth) ? "enabled, user=" + getString(profile.settings.login.username)
: "disabled");
String tz = getString(profile.settings.timezone); String tz = getString(profile.settings.timezone);
properties.put(ATTRIBUTE_TIMEZONE, properties.put(ATTRIBUTE_TIMEZONE,
(tz.isEmpty() ? "n/a" : tz) + ", auto-detect: " + getBool(profile.settings.tzautodetect)); (tz.isEmpty() ? "n/a" : tz) + ", auto-detect: " + getBool(profile.settings.tzautodetect));