Skip to content

Commit

Permalink
[shelly] Add Shelly Motion, minor improvements (openhab#10054)
Browse files Browse the repository at this point in the history
* Support for Shelly Motion, some minotr improvements, README updated

Signed-off-by: Markus Michels <markus7017@gmail.com>

* minor changes

Signed-off-by: Markus Michels <markus7017@gmail.com>

* Bug fixes from hardening

Signed-off-by: Markus Michels <markus7017@gmail.com>

* review changes applied

Signed-off-by: Markus Michels <markus7017@gmail.com>

* review change

Signed-off-by: Markus Michels <markus7017@gmail.com>

* review changes, fix creations of sensors#motion and device#externalPower
for H%T; moved images/uiroller*.png to doc/images

Signed-off-by: Markus Michels <markus7017@gmail.com>

* missing in last fix

Signed-off-by: Markus Michels <markus7017@gmail.com>
Signed-off-by: John Marshall <john.marshall.au@gmail.com>
  • Loading branch information
markus7017 authored and themillhousegroup committed May 10, 2021
1 parent b9488e5 commit 7601c0f
Show file tree
Hide file tree
Showing 31 changed files with 389 additions and 179 deletions.
18 changes: 18 additions & 0 deletions bundles/org.openhab.binding.shelly/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand Down
Binary file modified bundles/org.openhab.binding.shelly/doc/images/uiroller_fav1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified bundles/org.openhab.binding.shelly/doc/images/uiroller_obs1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified bundles/org.openhab.binding.shelly/doc/images/uiroller_obs2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified bundles/org.openhab.binding.shelly/doc/images/uiroller_obs3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified bundles/org.openhab.binding.shelly/doc/images/uiroller_wt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@
@NonNullByDefault
@Component(service = { ThingHandlerFactory.class, ShellyHandlerFactory.class }, configurationPid = "binding.shelly")
public class ShellyHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> 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<ShellyBaseHandler> deviceListeners = ConcurrentHashMap.newKeySet();
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = ShellyBindingConstants.SUPPORTED_THING_TYPES_UIDS;

private final Map<String, ShellyBaseHandler> deviceListeners = new ConcurrentHashMap<>();
private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration();
private String localIP = "";
private int httpPort = -1;
Expand Down Expand Up @@ -129,21 +131,28 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
}

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;
}

logger.debug("Unable to create Thing Handler instance!");
return null;
}

public Map<String, ShellyBaseHandler> 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);
}
}

Expand All @@ -158,8 +167,9 @@ protected synchronized void removeHandler(@NonNull ThingHandler thingHandler) {
public void onEvent(String ipAddress, String deviceName, String componentIndex, String eventType,
Map<String, String> parameters) {
logger.trace("{}: Dispatch event to thing handler", deviceName);
for (ShellyBaseHandler listener : deviceListeners) {
if (listener.onEvent(ipAddress, deviceName, componentIndex, eventType, parameters)) {
for (Map.Entry<String, ShellyBaseHandler> listener : deviceListeners.entrySet()) {
ShellyBaseHandler thingHandler = listener.getValue();
if (thingHandler.onEvent(ipAddress, deviceName, componentIndex, eventType, parameters)) {
// event processed
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -231,6 +232,11 @@ public static class ShellySettingsDevice {
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")
Expand Down Expand Up @@ -513,6 +519,8 @@ public static class ShellySettingsUpdate {
public String newVersion;
@SerializedName("old_version")
public String oldVersion;
@SerializedName("beta_version")
public String betaVersion;
}

public static class ShellySettingsGlobal {
Expand Down Expand Up @@ -540,6 +548,8 @@ public static class ShellySettingsGlobal {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -91,6 +92,8 @@ public class ShellyDeviceProfile {

public int updatePeriod = 2 * UPDATE_SETTINGS_INTERVAL_SECONDS + 10;

public String coiotEndpoint = "";

public Map<String, String> irCodes = new HashMap<>(); // Sense: list of stored IR codes

public ShellyDeviceProfile() {
Expand Down Expand Up @@ -188,13 +191,13 @@ public void initFromThingType(String name) {
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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
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;
Expand Down Expand Up @@ -228,6 +230,27 @@ public void setLightSetting(String parm, String value) throws ShellyApiException
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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescr
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,10 @@ public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen
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},
Expand Down
Loading

0 comments on commit 7601c0f

Please sign in to comment.