[shelly] Misc changes (small fixes, log improvements, hardened leak prevention on (#15922)

* Misc changes (same fixes, log improvements, hardened leak prevention on
exceptions)

---------

Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
Markus Michels 2023-11-25 09:46:22 +01:00 committed by GitHub
parent a31b1578be
commit a13fd80bfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 124 additions and 126 deletions

View File

@ -1451,8 +1451,6 @@ See notes on discovery of Shelly BLU devices above.
| | lowBattery | Switch | yes | Low battery alert (< 20%) |
| device | gatewayDevice | String | yes | Shelly forwarded last status update (BLU gateway), could vary from packet to packet |
### Shelly BLU Door/Window Sensor (thing-type: shellybludw)
See notes on discovery of Shelly BLU devices above.
@ -1467,7 +1465,7 @@ See notes on discovery of Shelly BLU devices above.
| | lowBattery | Switch | yes | Low battery alert (< 20%) |
| device | gatewayDevice | String | yes | Shelly forwarded last status update (BLU gateway), could vary from packet to packet |
## Shelly BLU Motion Sensor (thing-type: shellyblumotion)
### Shelly BLU Motion Sensor (thing-type: shellyblumotion)
See notes on discovery of Shelly BLU devices above.

View File

@ -14,13 +14,4 @@
<artifactId>org.openhab.binding.shelly</artifactId>
<name>openHAB Add-ons :: Bundles :: Shelly Binding Gen1+2</name>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
<version>9.4.46.v20220331</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -120,6 +120,7 @@ public class ShellyBindingConstants {
public static final String PROPERTY_DEV_TYPE = "deviceType";
public static final String PROPERTY_DEV_MODE = "deviceMode";
public static final String PROPERTY_DEV_GEN = "deviceGeneration";
public static final String PROPERTY_DEV_AUTH = "deviceAuth";
public static final String PROPERTY_GW_DEVICE = "gatewayDevice";
public static final String PROPERTY_HWREV = "deviceHwRev";
public static final String PROPERTY_HWBATCH = "deviceHwBatch";

View File

@ -119,7 +119,7 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
ShellyBaseHandler handler = null;
if (thingType.equals(THING_TYPE_SHELLYPROTECTED_STR)) {
logger.debug("{}: Create new thing of type {} using ShellyProtectedHandler", thing.getLabel(),
logger.debug("{}: Create new thing of type {} using ShellyProtectedHandler", thing.getLabel(),
thingTypeUID.toString());
handler = new ShellyProtectedHandler(thing, messages, bindingConfig, thingTable, coapServer, httpClient);
} else if (thingType.equals(THING_TYPE_SHELLYBULB_STR) || thingType.equals(THING_TYPE_SHELLYDUO_STR)

View File

@ -145,6 +145,7 @@ public class ShellyDeviceProfile {
device.hostname = device.mac.length() >= 12 ? "shelly-" + device.mac.toUpperCase().substring(6, 11)
: "unknown";
}
device.mode = getString(settings.mode).toLowerCase();
name = getString(settings.name);
hwRev = settings.hwinfo != null ? getString(settings.hwinfo.hwRevision) : "";
hwBatchId = settings.hwinfo != null ? getString(settings.hwinfo.batchId.toString()) : "";
@ -418,4 +419,18 @@ public class ShellyDeviceProfile {
// If device is not yet intialized or the enabled property is missing we assume that CoIoT is enabled
return true;
}
public static String buildBluServiceName(String name, String mac) throws IllegalArgumentException {
String model = name.contains("-") ? substringBefore(name, "-") : name; // e.g. SBBT-02C or just SBDW
switch (model) {
case SHELLYDT_BLUBUTTON:
return (THING_TYPE_SHELLYBLUBUTTON_STR + "-" + mac).toLowerCase();
case SHELLYDT_BLUDW:
return (THING_TYPE_SHELLYBLUDW_STR + "-" + mac).toLowerCase();
case SHELLYDT_BLUMOTION:
return (THING_TYPE_SHELLYBLUMOTION_STR + "-" + mac).toLowerCase();
default:
throw new IllegalArgumentException("Unsupported BLU device model " + model);
}
}
}

View File

@ -120,6 +120,12 @@ public class ShellyHttpClient {
}
return apiResult.response; // successful
} catch (ShellyApiException e) {
if (e.isHttpAccessUnauthorized() && !profile.isGen2 && !basicAuth && !config.password.isEmpty()) {
logger.debug("{}: Access is unauthorized, auto-activate basic auth", thingName);
basicAuth = true;
apiResult = innerRequest(HttpMethod.GET, uri, null, "");
}
if (e.isConnectionError()
|| (!e.isTimeout() && !apiResult.isHttpServerError()) && !apiResult.isNotFound()
|| profile.hasBattery || (retries == 0)) {
@ -129,9 +135,10 @@ public class ShellyHttpClient {
timeout = true;
timeoutErrors++; // count the retries
logger.debug("{}: API Timeout, retry #{} ({})", thingName, timeoutErrors, e.toString());
retries--;
if (profile.alwaysOn) {
logger.debug("{}: API Timeout, retry #{} ({})", thingName, timeoutErrors, e.toString());
}
}
}
throw new ShellyApiException("API Timeout or inconsistent result"); // successful

View File

@ -49,6 +49,7 @@ import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
import org.openhab.binding.shelly.internal.handler.ShellyColorUtils;
import org.openhab.binding.shelly.internal.handler.ShellyThingInterface;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -241,7 +242,7 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
}
if (!coiotBound) {
thingHandler.updateProperties(PROPERTY_COAP_VERSION, sVersion);
logger.debug("{}: CoIoT Version {} detected", thingName, iVersion);
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) {
@ -265,6 +266,13 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
}
}
// Don't change state to online when thing is in status config error
// (e.g. auth failed, but device sends COAP packets via multicast)
if (thingHandler.getThingStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR) {
logger.debug("{}: The device is not configuired correctly, skip Coap packet", thingName);
return;
}
// If we received a CoAP message successful the thing must be online
thingHandler.setThingOnline();
@ -441,7 +449,7 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
List<CoIotSensor> sensorUpdates = list.generic;
Map<String, State> updates = new TreeMap<String, State>();
logger.debug("{}: {} CoAP sensor updates received", thingName, sensorUpdates.size());
logger.debug("{}: {} CoAP sensor updates received", thingName, sensorUpdates.size());
int failed = 0;
ShellyColorUtils col = new ShellyColorUtils();
for (int i = 0; i < sensorUpdates.size(); i++) {

View File

@ -507,7 +507,7 @@ public class Shelly2ApiClient extends ShellyHttpClient {
rs.isValid = sm.isValid = emeter.isValid = true;
if (cs.state != null) {
if (!getString(rs.state).equals(cs.state)) {
logger.debug("{}: Roller status changed from {} to {}, updateChannels={}", thingName, rs.state,
logger.debug("{}: Roller status changed from {} to {}, updateChannels={}", thingName, rs.state,
mapValue(MAP_ROLLER_STATE, cs.state), updateChannels);
}
rs.state = mapValue(MAP_ROLLER_STATE, cs.state);

View File

@ -131,14 +131,13 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
@Override
public void initialize() throws ShellyApiException {
if (!initialized) {
rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.deviceIp);
rpcSocket.addMessageHandler(this);
initialized = true;
} else {
if (initialized) {
logger.debug("{}: Disconnect Rpc Socket on initialize", thingName);
disconnect();
}
rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.deviceIp);
rpcSocket.addMessageHandler(this);
initialized = true;
}
@Override
@ -1211,6 +1210,9 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
}
private void disconnect() {
if (rpcSocket.isConnected()) {
logger.debug("{}: Disconnect Rpc Socket", thingName);
}
rpcSocket.disconnect();
}
@ -1220,8 +1222,10 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
@Override
public void close() {
logger.debug("{}: Closing Rpc API (socket is {}, discovery={})", thingName,
rpcSocket.isConnected() ? "connected" : "disconnected", discovery);
if (initialized || rpcSocket.isConnected()) {
logger.debug("{}: Closing Rpc API (socket is {}, discovery={})", thingName,
rpcSocket.isConnected() ? "connected" : "disconnected", discovery);
}
disconnect();
initialized = false;
}

View File

@ -206,18 +206,24 @@ public class Shelly2RpcSocket {
if (s.isOpen()) {
logger.debug("{}: Disconnecting WebSocket ({} -> {})", thingName, s.getLocalAddress(),
s.getRemoteAddress());
s.disconnect();
}
s.disconnect();
s.close(StatusCode.NORMAL, "Socket closed");
session = null;
}
client.stop();
} catch (Exception e) {
if (e.getCause() instanceof InterruptedException) {
logger.debug("{}: Unable to close socket - interrupted", thingName); // e.g. device was rebooted
} else {
logger.debug("{}: Unable to close socket", thingName, e);
}
} finally {
// make sure client is stopped / thread terminates / socket resource is free up
try {
client.stop();
} catch (Exception e) {
logger.debug("{}: Unable to close Web Socket", thingName, e);
}
}
}

View File

@ -15,7 +15,6 @@ package org.openhab.binding.shelly.internal.api2;
import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
import static org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.*;
import static org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.*;
import static org.openhab.binding.shelly.internal.discovery.ShellyThingCreator.*;
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
import java.util.ArrayList;
@ -112,11 +111,11 @@ public class ShellyBluApi extends Shelly2ApiRpc {
public ShellySettingsDevice getDeviceInfo() throws ShellyApiException {
ShellySettingsDevice info = new ShellySettingsDevice();
info.hostname = !config.serviceName.isEmpty() ? config.serviceName : "";
info.fw = "1234";
info.type = "SBBT";
info.fw = "";
info.type = "BLU";
info.mac = config.deviceAddress;
info.auth = false;
info.gen = 99;
info.gen = 2;
return info;
}
@ -136,13 +135,13 @@ public class ShellyBluApi extends Shelly2ApiRpc {
profile.gateway = getThing().getProperty(PROPERTY_GW_DEVICE);
}
ShellySettingsDevice device = getDeviceInfo();
profile.device = getDeviceInfo();
if (config.serviceName.isEmpty()) {
config.serviceName = getString(profile.device.hostname);
}
profile.fwDate = substringBefore(device.fw, "/");
profile.fwVersion = substringBefore(ShellyDeviceProfile.extractFwVersion(device.fw.replace("/", "/v")), "-");
profile.status.update.oldVersion = profile.fwVersion;
// for now we have no API to get this information
profile.fwDate = profile.fwVersion = profile.status.update.oldVersion = "";
profile.status.hasUpdate = profile.status.update.hasUpdate = false;
if (profile.hasBattery) {
@ -239,7 +238,7 @@ public class ShellyBluApi extends Shelly2ApiRpc {
}
logger.debug("{}: BLU Device discovered", thingName);
if (e.data.name != null) {
profile.settings.name = buildBluServiceName(e.data.name, e.data.addr);
profile.settings.name = ShellyDeviceProfile.buildBluServiceName(e.data.name, e.data.addr);
}
break;
case SHELLY2_EVENT_BLUDATA:
@ -317,18 +316,4 @@ public class ShellyBluApi extends Shelly2ApiRpc {
if (updated) {
}
}
public static String buildBluServiceName(String name, String mac) throws IllegalArgumentException {
String model = name.contains("-") ? substringBefore(name, "-") : name; // e.g. SBBT-02C or just SBDW
switch (model) {
case SHELLYDT_BLUBUTTON:
return (THING_TYPE_SHELLYBLUBUTTON_STR + "-" + mac).toLowerCase();
case SHELLYDT_BLUDW:
return (THING_TYPE_SHELLYBLUDW_STR + "-" + mac).toLowerCase();
case SHELLYDT_BLUMOTION:
return (THING_TYPE_SHELLYBLUMOTION_STR + "-" + mac).toLowerCase();
default:
throw new IllegalArgumentException("Unsupported BLU device model " + model);
}
}
}

View File

@ -145,15 +145,18 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
boolean gen2 = "2".equals(service.getPropertyString("gen"));
ShellyApiInterface api = null;
boolean auth = false;
ShellySettingsDevice devInfo;
try {
api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient);
api.initialize();
devInfo = api.getDeviceInfo();
model = devInfo.type;
auth = devInfo.auth;
if (devInfo.name != null) {
deviceName = devInfo.name;
}
profile = api.getDeviceProfile(thingType, devInfo);
api.close();
logger.debug("{}: Shelly settings : {}", name, profile.settingsJson);
@ -191,6 +194,7 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
addProperty(properties, PROPERTY_DEV_TYPE, thingType);
addProperty(properties, PROPERTY_DEV_GEN, gen2 ? "2" : "1");
addProperty(properties, PROPERTY_DEV_MODE, mode);
addProperty(properties, PROPERTY_DEV_AUTH, auth ? "yes" : "no");
logger.debug("{}: Adding Shelly {}, UID={}", name, deviceName, thingUID.getAsString());
String thingLabel = deviceName.isEmpty() ? name + " - " + address

View File

@ -187,21 +187,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
config.eventsSensorReport, config.eventsCoIoT, bindingConfig.autoCoIoT);
start = initializeThing();
} catch (ShellyApiException e) {
ShellyApiResult res = e.getApiResult();
String mid = "";
if (e.isJsonError()) { // invalid JSON format
mid = "offline.status-error-unexpected-error";
start = false;
} else if (isAuthorizationFailed(res)) {
mid = "offline.conf-error-access-denied";
start = false;
} else if (profile.alwaysOn && e.isConnectionError()) {
mid = "offline.status-error-connect";
}
if (!mid.isEmpty()) {
setThingOffline(ThingStatusDetail.COMMUNICATION_ERROR, mid, e.toString());
}
logger.debug("{}: Unable to initialize: {}, retrying later", thingName, e.toString());
start = handleApiException(e);
} catch (IllegalArgumentException e) {
logger.debug("{}: Unable to initialize, retrying later", thingName, e);
} finally {
@ -215,6 +201,43 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
}, 2, TimeUnit.SECONDS);
}
private boolean handleApiException(ShellyApiException e) {
ShellyApiResult res = e.getApiResult();
ThingStatusDetail errorCode = ThingStatusDetail.COMMUNICATION_ERROR;
String status = "";
boolean retry = true;
if (e.isJsonError()) { // invalid JSON format
logger.debug("{}: Unable to parse API response: {}; json={}", thingName, res.getUrl(), res.response, e);
status = "offline.status-error-unexpected-error";
errorCode = ThingStatusDetail.CONFIGURATION_ERROR;
retry = false;
} else if (res.isHttpAccessUnauthorized()) {
status = "offline.conf-error-access-denied";
errorCode = ThingStatusDetail.CONFIGURATION_ERROR;
retry = false;
} else if (isWatchdogExpired()) {
status = "offline.status-error-watchdog";
} else if (res.httpCode >= 400) {
logger.debug("{}: Unexpected API result: {}/{}", thingName, res.httpCode, res.httpReason, e);
status = "offline.status-error-unexpected-api-result";
retry = false;
} else if (profile.alwaysOn && (e.isConnectionError() || res.isHttpTimeout())) {
status = "offline.status-error-connect";
}
if (!status.isEmpty()) {
setThingOffline(errorCode, status, e.toString());
} else {
logger.debug("{}: Unable to initialize: {}, retrying later", thingName, e.toString());
}
if (!retry) {
api.close();
}
return retry;
}
@Override
public ShellyThingConfiguration getThingConfig() {
return config;
@ -464,10 +487,11 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
requestUpdates(1, false);
}
} catch (ShellyApiException e) {
ShellyApiResult res = e.getApiResult();
if (isAuthorizationFailed(res)) {
if (!handleApiException(e)) {
return;
}
ShellyApiResult res = e.getApiResult();
if (res.isNotCalibrtated()) {
logger.warn("{}: {}", thingName, messages.get("roller.calibrating"));
} else {
@ -554,35 +578,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
} catch (ShellyApiException e) {
// http call failed: go offline except for battery devices, which might be in
// sleep mode. Once the next update is successful the device goes back online
String status = "";
ShellyApiResult res = e.getApiResult();
if (profile.alwaysOn && e.isConnectionError()) {
status = "offline.status-error-connect";
} else if (res.isHttpAccessUnauthorized()) {
status = "offline.conf-error-access-denied";
} else if (isWatchdogStarted()) {
if (!isWatchdogExpired()) {
logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
if (profile.alwaysOn) { // suppress for battery powered sensors
logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
}
}
} else if (e.isJSONException()) {
status = "offline.status-error-unexpected-api-result";
logger.debug("{}: Unable to parse API response: {}; json={}", thingName, res.getUrl(), res.response, e);
} else if (res.isHttpTimeout()) {
// Watchdog not started, e.g. device in sleep mode
if (isThingOnline()) { // ignore when already offline
status = "offline.status-error-watchdog";
}
} else {
status = "offline.status-error-unexpected-api-result";
logger.debug("{}: Unexpected API result: {}", thingName, res.response, e);
}
if (!status.isEmpty()) {
setThingOffline(ThingStatusDetail.COMMUNICATION_ERROR, status);
}
handleApiException(e);
} catch (NullPointerException | IllegalArgumentException e) {
logger.debug("{}: Unable to refresh status: {}", thingName, messages.get("statusupdate.failed"), e);
} finally {
@ -631,7 +627,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
}
if (prf.isRoller && prf.settings.favorites != null) {
String channelId = mkChannelId(CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_FAV);
logger.debug("{}: Adding {} roler favorite(s) to channel description", thingName,
logger.debug("{}: Adding {} roler favorite(s) to channel description", thingName,
prf.settings.favorites.size());
channelDefinitions.clearStateOptions(channelId);
int fid = 1;
@ -1057,7 +1053,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
String minVersion = !gen2 ? SHELLY_API_MIN_FWVERSION : SHELLY2_API_MIN_FWVERSION;
if (version.compare(prf.fwVersion, minVersion) < 0) {
logger.warn("{}: {}", prf.device.hostname,
messages.get("versioncheck.beta", prf.fwVersion, prf.fwDate));
messages.get("versioncheck.tooold", prf.fwVersion, prf.fwDate, minVersion));
}
}
if (!gen2 && bindingConfig.autoCoIoT && ((version.compare(prf.fwVersion, SHELLY_API_MIN_FWCOIOT)) >= 0)
@ -1120,23 +1116,6 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
}
}
/**
* Checks the http response for authorization error.
* If the authorization failed the binding can't access the device settings and determine the thing type. In this
* case the thing type shelly-unknown is set.
*
* @param result exception details including the http respone
* @return true if the authorization failed
*/
protected boolean isAuthorizationFailed(ShellyApiResult result) {
if (result.isHttpAccessUnauthorized()) {
// If the device is password protected the API doesn't provide settings to the device settings
setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-access-denied");
return true;
}
return false;
}
/**
* Change type of this thing.
*
@ -1363,11 +1342,11 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
properties.put(PROPERTY_SERVICE_NAME, config.serviceName);
String deviceName = getString(profile.settings.name);
properties.put(PROPERTY_SERVICE_NAME, config.serviceName);
properties.put(PROPERTY_DEV_GEN, "1");
properties.put(PROPERTY_DEV_GEN, !profile.isGen2 ? "1" : "2");
properties.put(PROPERTY_DEV_AUTH, getBool(profile.device.auth) ? "yes" : "no");
if (!deviceName.isEmpty()) {
properties.put(PROPERTY_DEV_NAME, deviceName);
}
properties.put(PROPERTY_DEV_GEN, !profile.isGen2 ? "1" : "2");
// add status properties
if (status.wifiSta != null) {

View File

@ -13,7 +13,6 @@
package org.openhab.binding.shelly.internal.handler;
import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
import static org.openhab.binding.shelly.internal.api2.ShellyBluApi.buildBluServiceName;
import static org.openhab.binding.shelly.internal.discovery.ShellyThingCreator.*;
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
import static org.openhab.core.thing.Thing.PROPERTY_MODEL_ID;
@ -23,6 +22,7 @@ import java.util.TreeMap;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2NotifyEvent;
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
@ -54,7 +54,7 @@ public class ShellyBluSensorHandler extends ShellyBaseHandler {
public static void addBluThing(String gateway, Shelly2NotifyEvent e, ShellyThingTable thingTable) {
String model = substringBefore(getString(e.data.name), "-").toUpperCase();
String mac = e.data.addr.replace(":", "");
String mac = e.data.addr.replaceAll(":", "");
String ttype = "";
logger.debug("{}: Create thing for new BLU device {}: {} / {}", gateway, e.data.name, model, mac);
ThingTypeUID tuid;
@ -75,7 +75,7 @@ public class ShellyBluSensorHandler extends ShellyBaseHandler {
logger.debug("{}: Unsupported BLU device model {}, MAC={}", gateway, model, mac);
return;
}
String serviceName = buildBluServiceName(model, mac);
String serviceName = ShellyDeviceProfile.buildBluServiceName(getString(e.data.name), mac);
Map<String, Object> properties = new TreeMap<>();
addProperty(properties, PROPERTY_MODEL_ID, model);

View File

@ -78,7 +78,7 @@ public class ShellyComponents {
if (status.tmp != null && getBool(status.tmp.isValid) && !thingHandler.getProfile().isSensor
&& status.tmp.tC != SHELLY_API_INVTEMP) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.tmp.tC), DIGITS_NONE, SIUnits.CELSIUS));
toQuantityType(getDouble(status.tmp.tC), DIGITS_TEMP, SIUnits.CELSIUS));
} else if (status.temperature != null && status.temperature != SHELLY_API_INVTEMP) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.temperature), DIGITS_NONE, SIUnits.CELSIUS));

View File

@ -64,7 +64,7 @@ public class ShellyManagerOverviewPage extends ShellyManagerPage {
String action = getUrlParm(parameters, URLPARM_ACTION).toLowerCase();
String uidParm = getUrlParm(parameters, URLPARM_UID).toLowerCase();
logger.debug("Generating overview for {} devices", getThingHandlers().size());
logger.debug("Generating overview for {} devices", getThingHandlers().size());
String html = "";
Map<String, String> properties = new HashMap<>();

View File

@ -315,7 +315,7 @@ public class ShellyChannelDefinitions {
addChannel(thing, add, profile.settings.sleepTime != null, CHGR_SENSOR, CHANNEL_SENSOR_SLEEPTIME);
// If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
boolean accuChannel = profile.numMeters > 1 && !profile.isRoller && !profile.isRGBW2;
boolean accuChannel = profile.hasRelays && profile.numMeters > 1 && !profile.isRoller && !profile.isRGBW2;
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);

View File

@ -34,7 +34,7 @@ message.versioncheck.update = INFO: New firmware available: current version: {0}
message.versioncheck.autocoiot = INFO: Firmware is full-filling the minimum version to auto-enable CoIoT
message.init.noipaddress = Unable to detect local IP address. Please make sure that IPv4 is enabled for this interface and check openHAB Network Configuration.
message.command.failed = ERROR: Unable to process command {0} for channel {1}
message.command.init = Thing not yet initialized, command {0} triggered initialization
message.command.init = Thing not yet initialized, command {0} triggered initialization
message.status.unknown.initializing = Initializing or device in sleep mode.
message.statusupdate.failed = Unable to update status
message.status.managerstarted = Shelly Manager started at http(s)://{0}:{1}/shelly/manager
@ -120,7 +120,7 @@ thing-type.shelly.shellyproem50.description = Shelly Pro EM-50 - 2xPower Meter +
thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch with Power Meter
# BLU devices
thing-type.shelly.shellyblubutton.description = Shelly BLU Button
thing-type.shelly.shellyblubutton.description = Shelly BLU Button 1
thing-type.shelly.shellybludw.description = Shelly BLU Door/Window Sensor
thing-type.shelly.shellyblumotion.description = Shelly BLU Motion Sensor
@ -247,7 +247,7 @@ channel-type.shelly.temperature4.description = Temperature of external Sensor #4
channel-type.shelly.temperature5.label = Temperature 5
channel-type.shelly.temperature6.description = Temperature of external Sensor #5
channel-type.shelly.targetTemp.label = Target Temperature
channel-type.shelly.targetTemp.description = Target Temperature in °C to be reached in auto-temperature mode
channel-type.shelly.targetTemp.description = Target Temperature in °C to be reached in auto-temperature mode
channel-type.shelly.humidity.label = Humidity
channel-type.shelly.humidity.description = Relative humidity (0..100%)
channel-type.shelly.rollerShutter.label = Roller Control (0=open, 100=closed)