diff --git a/bundles/org.openhab.binding.shelly/README.md b/bundles/org.openhab.binding.shelly/README.md index 9006b94dd..5750f1ff2 100644 --- a/bundles/org.openhab.binding.shelly/README.md +++ b/bundles/org.openhab.binding.shelly/README.md @@ -30,6 +30,7 @@ Refer to [Advanced Users](doc/AdvancedUsers.md) for more information on openHAB | shellydimmer | Shelly Dimmer | SHDM-1 | | shellydimmer2 | Shelly Dimmer2 | SHDM-2 | | shellyix3 | Shelly ix3 | SHIX3-1 | +| shellyuni | Shelly UNI | SHUNI-1 | | shellyplug | Shelly Plug | SHPLG2-1 | | shellyplugs | Shelly Plug-S | SHPLG-S | | shellyem | Shelly EM with integrated Power Meters | SHEM | @@ -578,6 +579,23 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the | |lastEvent |String |yes |S/SS/SSS for 1/2/3x Shortpush or L for Longpush | | |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. | +### Shelly UNI - Low voltage sensor/actor: shellyuni) + +|Group |Channel |Type |read-only|Description | +|----------|-------------|---------|---------|----------------------------------------------------------------------------| +|relay1 | | | |See group relay1 for Shelly 2, no autoOn/autoOff/timerActive channels | +|relay2 | | | |See group relay1 for Shelly 2, no autoOn/autoOff/timerActive channels | +|sensors |temperature1 |Number |yes |Temperature value of external sensor #1 (if connected to temp/hum addon) | +| |temperature2 |Number |yes |Temperature value of external sensor #2 (if connected to temp/hum addon) | +| |temperature3 |Number |yes |Temperature value of external sensor #3 (if connected to temp/hum addon) | +| |humidity |Number |yes |Humidity in percent (if connected to temp/hum addon) | +| |voltage |Number |yes |ADCS voltage | +|status |input1 |Switch |yes |State of Input 1 | +| |input2 |Switch |yes |State of Input 2 | +| |button |Trigger |yes |Event trigger, see section Button Events | +| |lastEvent |String |yes |S/SS/SSS for 1/2/3x Shortpush or L for Longpush | +| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. | + ### Shelly Bulb (thing-type: shellybulb) |Group |Channel |Type |read-only|Description | diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav1.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav1.png index 8a2395c77..0d09bdc7e 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav1.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav1.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav2.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav2.png index d80b49e72..af4ef7a5e 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav2.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_fav2.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs1.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs1.png index 112b64096..2dd54901f 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs1.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs1.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs2.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs2.png index 5cb5a56cb..c0b01c523 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs2.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs2.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs3.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs3.png index 3c970c05f..fc9296d91 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs3.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_obs3.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_rlogin.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_rlogin.png index 628bfdb76..799d64c50 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_rlogin.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_rlogin.png differ diff --git a/bundles/org.openhab.binding.shelly/doc/images/uiroller_wt.png b/bundles/org.openhab.binding.shelly/doc/images/uiroller_wt.png index c6432cdb9..07888025e 100644 Binary files a/bundles/org.openhab.binding.shelly/doc/images/uiroller_wt.png and b/bundles/org.openhab.binding.shelly/doc/images/uiroller_wt.png differ diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java index c3bf59eaa..e17a1c715 100755 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java @@ -164,7 +164,7 @@ public class ShellyBindingConstants { THING_TYPE_SHELLYVINTAGE, THING_TYPE_SHELLYDUORGBW, THING_TYPE_SHELLYRGBW2_COLOR, THING_TYPE_SHELLYRGBW2_WHITE, THING_TYPE_SHELLYHT, THING_TYPE_SHELLYSENSE, THING_TYPE_SHELLYEYE, THING_TYPE_SHELLYSMOKE, THING_TYPE_SHELLYGAS, THING_TYPE_SHELLYFLOOD, THING_TYPE_SHELLYDOORWIN, - THING_TYPE_SHELLYDOORWIN2, THING_TYPE_SHELLYBUTTON1, /* THING_TYPE_SHELLMOTION, */ + THING_TYPE_SHELLYDOORWIN2, THING_TYPE_SHELLYBUTTON1, THING_TYPE_SHELLMOTION, THING_TYPE_SHELLYPROTECTED, THING_TYPE_SHELLYUNKNOWN).collect(Collectors.toSet())); // Thing Configuration Properties @@ -333,6 +333,7 @@ public class ShellyBindingConstants { public static final String ALARM_TYPE_OVERPOWER = "OVERPOWER"; public static final String ALARM_TYPE_OVERLOAD = "OVERLOAD"; public static final String ALARM_TYPE_LOADERR = "LOAD_ERROR"; + public static final String ALARM_TYPE_SENSOR_ERROR = "SENSOR_ERROR"; public static final String ALARM_TYPE_LOW_BATTERY = "LOW_BATTERY"; // Event types diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyHandlerFactory.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyHandlerFactory.java index 9e0d3c02b..5708d30ca 100755 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyHandlerFactory.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyHandlerFactory.java @@ -54,12 +54,14 @@ import io.reactivex.annotations.NonNull; @NonNullByDefault @Component(service = { ThingHandlerFactory.class, ShellyHandlerFactory.class }, configurationPid = "binding.shelly") public class ShellyHandlerFactory extends BaseThingHandlerFactory { + private static final Set SUPPORTED_THING_TYPES_UIDS = ShellyBindingConstants.SUPPORTED_THING_TYPES_UIDS; + private final Logger logger = LoggerFactory.getLogger(ShellyHandlerFactory.class); private final HttpClient httpClient; private final ShellyTranslationProvider messages; private final ShellyCoapServer coapServer; - private final Set deviceListeners = ConcurrentHashMap.newKeySet(); - private static final Set SUPPORTED_THING_TYPES_UIDS = ShellyBindingConstants.SUPPORTED_THING_TYPES_UIDS; + + private final Map deviceListeners = new ConcurrentHashMap<>(); private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration(); private String localIP = ""; private int httpPort = -1; @@ -129,7 +131,9 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory { } if (handler != null) { - deviceListeners.add(handler); + String uid = thing.getUID().getAsString(); + deviceListeners.put(uid, handler); + logger.debug("Thing handler for uid {} added, total things = {}", uid, deviceListeners.size()); return handler; } @@ -137,13 +141,18 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory { return null; } + public Map getThingHandlers() { + return deviceListeners; + } + /** * Remove handler of things. */ @Override protected synchronized void removeHandler(@NonNull ThingHandler thingHandler) { if (thingHandler instanceof ShellyBaseHandler) { - deviceListeners.remove(thingHandler); + String uid = thingHandler.getThing().getUID().getAsString(); + deviceListeners.remove(uid); } } @@ -158,8 +167,9 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory { public void onEvent(String ipAddress, String deviceName, String componentIndex, String eventType, Map parameters) { logger.trace("{}: Dispatch event to thing handler", deviceName); - for (ShellyBaseHandler listener : deviceListeners) { - if (listener.onEvent(ipAddress, deviceName, componentIndex, eventType, parameters)) { + for (Map.Entry listener : deviceListeners.entrySet()) { + ShellyBaseHandler thingHandler = listener.getValue(); + if (thingHandler.onEvent(ipAddress, deviceName, componentIndex, eventType, parameters)) { // event processed return; } diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyApiJsonDTO.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyApiJsonDTO.java index 739861d62..bb460bb32 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyApiJsonDTO.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyApiJsonDTO.java @@ -35,6 +35,7 @@ public class ShellyApiJsonDTO { public static final String SHELLY_URL_SETTINGS_CLOUD = "/settings/cloud"; public static final String SHELLY_URL_LIST_IR = "/ir/list"; public static final String SHELLY_URL_SEND_IR = "/ir/emit"; + public static final String SHELLY_URL_RESTART = "/reboot"; public static final String SHELLY_URL_SETTINGS_RELAY = "/settings/relay"; public static final String SHELLY_URL_STATUS_RELEAY = "/status/relay"; @@ -231,6 +232,11 @@ public class ShellyApiJsonDTO { public String hostname; public String fw; public Boolean auth; + + @SerializedName("coiot") // Shelly Motion Multicast Endpoint + public String coiot; + public Integer longid; + @SerializedName("num_outputs") public Integer numOutputs; @SerializedName("num_meters") @@ -513,6 +519,8 @@ public class ShellyApiJsonDTO { public String newVersion; @SerializedName("old_version") public String oldVersion; + @SerializedName("beta_version") + public String betaVersion; } public static class ShellySettingsGlobal { @@ -540,6 +548,8 @@ public class ShellyApiJsonDTO { ShellyStatusCloud cloud; @SerializedName("sleep_mode") public ShellySensorSleepMode sleepMode; // FW 1.6 + @SerializedName("external_power") + public Integer externalPower; // H&T FW 1.6, seems to be the same like charger for the Sense public String timezone; public Double lat; diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyDeviceProfile.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyDeviceProfile.java index 00cdb38fa..74895c0e8 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyDeviceProfile.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyDeviceProfile.java @@ -81,6 +81,7 @@ public class ShellyDeviceProfile { public boolean isSensor = false; // true for HT & Smoke public boolean hasBattery = false; // true if battery device public boolean isSense = false; // true if thing is a Shelly Sense + public boolean isMotion = false; // true if thing is a Shelly Sense public boolean isHT = false; // true for H&T public boolean isDW = false; // true for Door Window sensor public boolean isButton = false; // true for a Shelly Button 1 @@ -91,6 +92,8 @@ public class ShellyDeviceProfile { public int updatePeriod = 2 * UPDATE_SETTINGS_INTERVAL_SECONDS + 10; + public String coiotEndpoint = ""; + public Map irCodes = new HashMap<>(); // Sense: list of stored IR codes public ShellyDeviceProfile() { @@ -188,13 +191,13 @@ public class ShellyDeviceProfile { boolean isSmoke = thingType.equals(THING_TYPE_SHELLYSMOKE_STR); boolean isGas = thingType.equals(THING_TYPE_SHELLYGAS_STR); boolean isUNI = thingType.equals(THING_TYPE_SHELLYUNI_STR); - boolean isMotion = thingType.equals(THING_TYPE_SHELLYMOTION_STR); isHT = thingType.equals(THING_TYPE_SHELLYHT_STR); isDW = thingType.equals(THING_TYPE_SHELLYDOORWIN_STR) || thingType.equals(THING_TYPE_SHELLYDOORWIN2_STR); + isMotion = thingType.startsWith(THING_TYPE_SHELLYMOTION_STR); isSense = thingType.equals(THING_TYPE_SHELLYSENSE_STR); isIX3 = thingType.equals(THING_TYPE_SHELLYIX3_STR); isButton = thingType.equals(THING_TYPE_SHELLYBUTTON1_STR); - isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isUNI || isSense; + isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isUNI || isMotion || isSense; hasBattery = isHT || isFlood || isDW || isSmoke || isButton || isMotion; // we assume that Sense is connected to // the charger } diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpApi.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpApi.java index 7a383f2a0..19ef1f1e0 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpApi.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpApi.java @@ -36,7 +36,9 @@ import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySendKeyLis import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySenseKeyCode; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsDevice; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsLight; +import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsLogin; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus; +import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsUpdate; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyShortLightStatus; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLight; import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay; @@ -228,6 +230,27 @@ public class ShellyHttpApi { request(SHELLY_URL_SETTINGS + "?" + parm + "=" + value); } + public ShellySettingsLogin getLoginSettings() throws ShellyApiException { + return callApi(SHELLY_URL_SETTINGS + "/login", ShellySettingsLogin.class); + } + + public ShellySettingsLogin setLoginCredentials(String user, String password) throws ShellyApiException { + return callApi(SHELLY_URL_SETTINGS + "/login?enabled=yes&username=" + user + "&password=" + password, + ShellySettingsLogin.class); + } + + public String deviceReboot() throws ShellyApiException { + return callApi(SHELLY_URL_RESTART, String.class); + } + + public String factoryReset() throws ShellyApiException { + return callApi(SHELLY_URL_SETTINGS + "?reset=true", String.class); + } + + public ShellySettingsUpdate firmwareUpdate(String uri) throws ShellyApiException { + return callApi("/ota?" + uri, ShellySettingsUpdate.class); + } + /** * Change between White and Color Mode * diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTProtocol.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTProtocol.java index db9d7f614..7161f413d 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTProtocol.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTProtocol.java @@ -83,7 +83,7 @@ public class ShellyCoIoTProtocol { switch (sen.type.toLowerCase()) { case "b": // BatteryLevel + updateChannel(updates, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL, - toQuantityType(s.value, DIGITS_PERCENT, Units.PERCENT)); + toQuantityType(s.value, 0, Units.PERCENT)); break; case "h" /* Humidity */: updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM, diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTVersion2.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTVersion2.java index 78e2cc7f0..8fed7e315 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTVersion2.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoIoTVersion2.java @@ -297,8 +297,10 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo break; case "3119": // Motion timestamp // {"I":3119,"T":"S","D":"timestamp","U":"s","R":["U32","-1"],"L":1}, - updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION_TS, - getTimestamp(getString(profile.settings.timezone), (long) s.value)); + if (s.value != 0) { + updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION_TS, + getTimestamp(getString(profile.settings.timezone), (long) s.value)); + } break; case "3120": // motionActive // {"I":3120,"T":"S","D":"motionActive","R":["0/1","-1"],"L":1}, diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoapHandler.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoapHandler.java index 8b2db8410..378c5730d 100644 --- a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoapHandler.java +++ b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/coap/ShellyCoapHandler.java @@ -81,7 +81,10 @@ public class ShellyCoapHandler implements ShellyCoapListener { private Request reqDescription = new Request(Code.GET, Type.CON); private Request reqStatus = new Request(Code.GET, Type.CON); private boolean discovering = false; + private int coiotPort = COIOT_PORT; + private long coiotMessages = 0; + private long coiotErrors = 0; private int lastSerial = -1; private String lastPayload = ""; private Map blkMap = new LinkedHashMap<>(); @@ -118,8 +121,12 @@ public class ShellyCoapHandler implements ShellyCoapListener { } logger.debug("{}: Starting CoAP Listener", thingName); - coapServer.start(config.localIp, this); - statusClient = new CoapClient(completeUrl(config.deviceIp, COLOIT_URI_DEVSTATUS)) + if (!profile.coiotEndpoint.isEmpty() && profile.coiotEndpoint.contains(":")) { + String ps = substringAfter(profile.coiotEndpoint, ":"); + coiotPort = Integer.parseInt(ps); + } + coapServer.start(config.localIp, coiotPort, this); + statusClient = new CoapClient(completeUrl(config.deviceIp, coiotPort, COLOIT_URI_DEVSTATUS)) .setTimeout((long) SHELLY_API_TIMEOUT_MS).useNONs().setEndpoint(coapServer.getEndpoint()); @Nullable Endpoint endpoint = null; @@ -152,10 +159,39 @@ public class ShellyCoapHandler implements ShellyCoapListener { @Override public void processResponse(@Nullable Response response) { if (response == null) { + coiotErrors++; return; // other device instance } + ResponseCode code = response.getCode(); + if (code != ResponseCode.CONTENT) { + // error handling + logger.debug("{}: Unknown Response Code {} received, payload={}", thingName, code, + response.getPayloadString()); + coiotErrors++; + return; + } + + List