Skip to content

Commit

Permalink
[shelly] BLU Motion, optimize ShellyManager for BLU devices (openhab#…
Browse files Browse the repository at this point in the history
…15401)

Signed-off-by: Markus Michels <[email protected]>
  • Loading branch information
markus7017 authored and andrewfg committed Nov 26, 2023
1 parent 822181d commit 8f83521
Show file tree
Hide file tree
Showing 17 changed files with 133 additions and 70 deletions.
15 changes: 14 additions & 1 deletion bundles/org.openhab.binding.shelly/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ The binding supports both hardware generations

- Generation 1: The original Shelly devices like the Shelly 1, Shelly 2.5, Shelly Flood etc.
- Generation 2: The new Plus / Pro series of devices
- Shelly Plus Mini: Shelly Plus devices in compact format
- Shelly BLU: Bluetooth based series of devices

The binding provides the same feature set across all devices as good as possible and depending on device specific features.

Expand Down Expand Up @@ -120,7 +122,7 @@ The binding provides the same feature set across all devices as good as possible
| ----------------- | ------------------------------------------------------ | --------- |
| shellyblubutton | Shelly BLU Button 1 | SBBT |
| shellybludw | Shelly BLU Door/Windows | SBDW |

| shellyblumotion | Shelly BLU Motion | SBMO |

## Binding Configuration

Expand Down Expand Up @@ -1465,6 +1467,17 @@ 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)

See notes on discovery of Shelly BLU devices above.

| Group | Channel | Type | read-only | Description |
| ------- | ------------- | -------- | --------- | ----------------------------------------------------------------------------------- |
| sensors | motion | Switch | yes | ON: Motion detected |
| battery | batteryLevel | Number | yes | Battery Level in % |
| | 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 Wall Displays

| Group | Channel | Type | read-only | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public class ShellyBindingConstants {
// Shelly BLU
THING_TYPE_SHELLYBLUBUTTON, //
THING_TYPE_SHELLYBLUDW, //
THING_TYPE_SHELLYBLUMOTION, //

THING_TYPE_SHELLYPROTECTED, //
THING_TYPE_SHELLYUNKNOWN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ public void initFromThingType(String name) {
isHT = thingType.equals(THING_TYPE_SHELLYHT_STR) || thingType.equals(THING_TYPE_SHELLYPLUSHT_STR);
isDW = thingType.equals(THING_TYPE_SHELLYDOORWIN_STR) || thingType.equals(THING_TYPE_SHELLYDOORWIN2_STR)
|| thingType.equals(THING_TYPE_SHELLYBLUDW_STR);
isMotion = thingType.startsWith(THING_TYPE_SHELLYMOTION_STR);
isMotion = thingType.startsWith(THING_TYPE_SHELLYMOTION_STR)
|| thingType.equals(THING_TYPE_SHELLYBLUMOTION_STR);
isSense = thingType.equals(THING_TYPE_SHELLYSENSE_STR);
isIX = thingType.equals(THING_TYPE_SHELLYIX3_STR) || thingType.equals(THING_TYPE_SHELLYPLUSI4_STR)
|| thingType.equals(THING_TYPE_SHELLYPLUSI4DC_STR);
Expand Down Expand Up @@ -401,7 +402,8 @@ public static String extractFwVersion(@Nullable String version) {
}

public static boolean isGeneration2(String thingType) {
return thingType.startsWith("shellyplus") || thingType.startsWith("shellypro") || isBluSeries(thingType);
return thingType.startsWith("shellyplus") || thingType.startsWith("shellypro")
|| thingType.startsWith("shellymini") || isBluSeries(thingType);
}

public static boolean isBluSeries(String thingType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

Expand Down Expand Up @@ -102,57 +101,46 @@ public Shelly2ApiClient(String thingName, ShellyThingConfiguration config, HttpC
super(thingName, config, httpClient);
}

protected static final Map<String, String> MAP_INMODE_BTNTYPE = new HashMap<>();
static {
MAP_INMODE_BTNTYPE.put(SHELLY2_BTNT_MOMENTARY, SHELLY_BTNT_MOMENTARY);
MAP_INMODE_BTNTYPE.put(SHELLY2_BTNT_FLIP, SHELLY_BTNT_TOGGLE);
MAP_INMODE_BTNTYPE.put(SHELLY2_BTNT_FOLLOW, SHELLY_BTNT_EDGE);
MAP_INMODE_BTNTYPE.put(SHELLY2_BTNT_DETACHED, SHELLY_BTNT_MOMENTARY);
}

protected static final Map<String, String> MAP_INPUT_EVENT_TYPE = new HashMap<>();
static {
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_1PUSH, SHELLY_BTNEVENT_1SHORTPUSH);
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_2PUSH, SHELLY_BTNEVENT_2SHORTPUSH);
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_3PUSH, SHELLY_BTNEVENT_3SHORTPUSH);
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_LPUSH, SHELLY_BTNEVENT_LONGPUSH);
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_LSPUSH, SHELLY_BTNEVENT_LONGSHORTPUSH);
MAP_INPUT_EVENT_TYPE.put(SHELLY2_EVENT_SLPUSH, SHELLY_BTNEVENT_SHORTLONGPUSH);
}

protected static final Map<String, String> MAP_INPUT_EVENT_ID = new HashMap<>();
static {
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_BTNUP, SHELLY_EVENT_BTN_OFF);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_BTNDOWN, SHELLY_EVENT_BTN_ON);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_1PUSH, SHELLY_EVENT_SHORTPUSH);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_2PUSH, SHELLY_EVENT_DOUBLE_SHORTPUSH);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_3PUSH, SHELLY_EVENT_TRIPLE_SHORTPUSH);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_LPUSH, SHELLY_EVENT_LONGPUSH);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_LSPUSH, SHELLY_EVENT_LONG_SHORTPUSH);
MAP_INPUT_EVENT_ID.put(SHELLY2_EVENT_SLPUSH, SHELLY_EVENT_SHORT_LONGTPUSH);
}

protected static final Map<String, String> MAP_INPUT_MODE = new HashMap<>();
static {
MAP_INPUT_MODE.put(SHELLY2_RMODE_SINGLE, SHELLY_INP_MODE_ONEBUTTON);
MAP_INPUT_MODE.put(SHELLY2_RMODE_DUAL, SHELLY_INP_MODE_OPENCLOSE);
MAP_INPUT_MODE.put(SHELLY2_RMODE_DETACHED, SHELLY_INP_MODE_ONEBUTTON);
}

protected static final Map<String, String> MAP_ROLLER_STATE = new HashMap<>();
static {
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_OPEN, SHELLY_RSTATE_OPEN);
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_CLOSED, SHELLY_RSTATE_CLOSE);
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_OPENING, SHELLY2_RSTATE_OPENING); // Gen2-only
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_CLOSING, SHELLY2_RSTATE_CLOSING); // Gen2-only
MAP_ROLLER_STATE.put(SHELLY2_RSTATE_STOPPED, SHELLY_RSTATE_STOP);
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 static final Map<String, String> MAP_INMODE_BTNTYPE = Map.of(//
SHELLY2_BTNT_MOMENTARY, SHELLY_BTNT_MOMENTARY, //
SHELLY2_BTNT_FLIP, SHELLY_BTNT_TOGGLE, //
SHELLY2_BTNT_FOLLOW, SHELLY_BTNT_EDGE, //
SHELLY2_BTNT_DETACHED, SHELLY_BTNT_MOMENTARY);

protected static final Map<String, String> MAP_INPUT_EVENT_TYPE = Map.of(//
SHELLY2_EVENT_1PUSH, SHELLY_BTNEVENT_1SHORTPUSH, //
SHELLY2_EVENT_2PUSH, SHELLY_BTNEVENT_2SHORTPUSH, //
SHELLY2_EVENT_3PUSH, SHELLY_BTNEVENT_3SHORTPUSH, //
SHELLY2_EVENT_LPUSH, SHELLY_BTNEVENT_LONGPUSH, //
SHELLY2_EVENT_LSPUSH, SHELLY_BTNEVENT_LONGSHORTPUSH, //
SHELLY2_EVENT_SLPUSH, SHELLY_BTNEVENT_SHORTLONGPUSH);

protected static final Map<String, String> MAP_INPUT_EVENT_ID = Map.of(//
SHELLY2_EVENT_BTNUP, SHELLY_EVENT_BTN_OFF, //
SHELLY2_EVENT_BTNDOWN, SHELLY_EVENT_BTN_ON, //
SHELLY2_EVENT_1PUSH, SHELLY_EVENT_SHORTPUSH, //
SHELLY2_EVENT_2PUSH, SHELLY_EVENT_DOUBLE_SHORTPUSH, //
SHELLY2_EVENT_3PUSH, SHELLY_EVENT_TRIPLE_SHORTPUSH, //
SHELLY2_EVENT_LPUSH, SHELLY_EVENT_LONGPUSH, //
SHELLY2_EVENT_LSPUSH, SHELLY_EVENT_LONG_SHORTPUSH, //
SHELLY2_EVENT_SLPUSH, SHELLY_EVENT_SHORT_LONGTPUSH);

protected static final Map<String, String> MAP_INPUT_MODE = Map.of(//
SHELLY2_RMODE_SINGLE, SHELLY_INP_MODE_ONEBUTTON, //
SHELLY2_RMODE_DUAL, SHELLY_INP_MODE_OPENCLOSE, //
SHELLY2_RMODE_DETACHED, SHELLY_INP_MODE_ONEBUTTON);

protected static final Map<String, String> MAP_ROLLER_STATE = Map.of(//
SHELLY2_RSTATE_OPEN, SHELLY_RSTATE_OPEN, //
SHELLY2_RSTATE_CLOSED, SHELLY_RSTATE_CLOSE, //
SHELLY2_RSTATE_OPENING, SHELLY2_RSTATE_OPENING, // Gen2-only
SHELLY2_RSTATE_CLOSING, SHELLY2_RSTATE_CLOSING, // Gen2-only
SHELLY2_RSTATE_STOPPED, SHELLY_RSTATE_STOP, //
SHELLY2_RSTATE_CALIB, SHELLY2_RSTATE_CALIB); // Gen2-only

protected static final Map<String, String> MAP_PROFILE = Map.of(//
SHELLY_CLASS_RELAY, SHELLY2_PROFILE_RELAY, //
SHELLY_CLASS_ROLLER, SHELLY2_PROFILE_COVER);

protected @Nullable ArrayList<@Nullable ShellySettingsRelay> fillRelaySettings(ShellyDeviceProfile profile,
Shelly2GetConfigResult dc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,10 @@ public class Shelly2NotifyEventMessage {
public Integer windowState;
@SerializedName("Rotation")
public Double rotation;
@SerializedName("Motion")
public Integer motionState;
@SerializedName("Temperature")
public Double temperature;

public Integer rssi;
public Integer tx_power;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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.ShellySensorSleepMode;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySensorTmp;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsInput;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
Expand Down Expand Up @@ -271,12 +272,22 @@ public void onNotifyEvent(Shelly2RpcNotifyEvent message) {
sensorData.lux.isValid = true;
sensorData.lux.value = (double) e.data.illuminance;
}
if (e.data.temperature != null) {
if (sensorData.tmp == null) {
sensorData.tmp = new ShellySensorTmp();
}
sensorData.tmp.tC = e.data.temperature;
sensorData.tmp.isValid = true;
}
if (e.data.rotation != null) {
if (sensorData.accel == null) {
sensorData.accel = new ShellySensorAccel();
}
sensorData.accel.tilt = e.data.rotation.intValue();
}
if (e.data.motionState != null) {
sensorData.motion = e.data.motionState == 1;
}

if (e.data.buttonEvent != null) {
ShellyInputState input = deviceStatus.inputs != null ? deviceStatus.inputs.get(0)
Expand Down Expand Up @@ -314,6 +325,8 @@ public static String buildBluServiceName(String name, String mac) throws Illegal
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public class ShellyThingCreator {
// Shelly BLU Series
public static final String SHELLYDT_BLUBUTTON = "SBBT";
public static final String SHELLYDT_BLUDW = "SBDW";
public static final String SHELLYDT_BLUMOTION = "SBMO";

// Thing names
public static final String THING_TYPE_SHELLY1_STR = "shelly1";
Expand Down Expand Up @@ -191,6 +192,7 @@ public class ShellyThingCreator {
public static final String THING_TYPE_SHELLYBLU_PREFIX = "shellyblu";
public static final String THING_TYPE_SHELLYBLUBUTTON_STR = THING_TYPE_SHELLYBLU_PREFIX + "button";
public static final String THING_TYPE_SHELLYBLUDW_STR = THING_TYPE_SHELLYBLU_PREFIX + "dw";
public static final String THING_TYPE_SHELLYBLUMOTION_STR = THING_TYPE_SHELLYBLU_PREFIX + "motion";

// Password protected or unknown device
public static final String THING_TYPE_SHELLYPROTECTED_STR = "shellydevice";
Expand Down Expand Up @@ -306,6 +308,8 @@ public class ShellyThingCreator {
public static final ThingTypeUID THING_TYPE_SHELLYBLUBUTTON = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYBLUBUTTON_STR);
public static final ThingTypeUID THING_TYPE_SHELLYBLUDW = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYBLUDW_STR);
public static final ThingTypeUID THING_TYPE_SHELLYBLUMOTION = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYBLUMOTION_STR);

private static final Map<String, String> THING_TYPE_MAPPING = new LinkedHashMap<>();
static {
Expand Down Expand Up @@ -383,6 +387,7 @@ public class ShellyThingCreator {
// BLU Series
THING_TYPE_MAPPING.put(SHELLYDT_BLUBUTTON, THING_TYPE_SHELLYBLUBUTTON_STR);
THING_TYPE_MAPPING.put(SHELLYDT_BLUDW, THING_TYPE_SHELLYBLUDW_STR);
THING_TYPE_MAPPING.put(SHELLYDT_BLUMOTION, THING_TYPE_SHELLYBLUMOTION_STR);

// Wall displays
THING_TYPE_MAPPING.put(SHELLYDT_PLUSWALLDISPLAY, THING_TYPE_SHELLYPLUSWALLDISPLAY_STR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,7 @@ protected void initializeThingConfig() {
config.password = bindingConfig.defaultPassword;
logger.debug("{}: Using default password from bindingConfig (userId={})", thingName, config.userId);
}

if (config.updateInterval == 0) {
config.updateInterval = UPDATE_STATUS_INTERVAL_SECONDS * UPDATE_SKIP_COUNT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public static void addBluThing(String gateway, Shelly2NotifyEvent e, ShellyThing
ttype = THING_TYPE_SHELLYBLUDW_STR;
tuid = THING_TYPE_SHELLYBLUDW;
break;
case SHELLYDT_BLUMOTION:
ttype = THING_TYPE_SHELLYBLUMOTION_STR;
tuid = THING_TYPE_SHELLYBLUMOTION;
break;
default:
logger.debug("{}: Unsupported BLU device model {}, MAC={}", gateway, model, mac);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,9 @@ public static Map<String, String> getActions(ShellyDeviceProfile profile) {
boolean gen2 = profile.isGen2;

list.put(ACTION_RES_STATS, "Reset Statistics");
list.put(ACTION_RESTART, "Reboot Device");
if (!profile.isBlu) {
list.put(ACTION_RESTART, "Reboot Device");
}
if (!gen2 || !profile.isBlu) {
list.put(ACTION_PROTECT, "Protect Device");
}
Expand Down Expand Up @@ -413,10 +415,13 @@ && getBool(profile.settings.wifiSta.enabled)) {
!profile.settings.bluetooth ? "Enable Bluetooth" : "Disable Bluetooth");
}

boolean set = profile.settings.cloud != null && getBool(profile.settings.cloud.enabled);
list.put(set ? ACTION_DISCLOUD : ACTION_ENCLOUD, set ? "Disable Cloud" : "Enable Cloud");
if (!profile.isBlu) {
boolean set = profile.settings.cloud != null && getBool(profile.settings.cloud.enabled);
list.put(set ? ACTION_DISCLOUD : ACTION_ENCLOUD, set ? "Disable Cloud" : "Enable Cloud");

list.put(ACTION_RESET, "-Factory Reset");
}

list.put(ACTION_RESET, "-Factory Reset");
if (!gen2 && profile.extFeatures) {
list.put(ACTION_OTACHECK, "Check for Update");
boolean debug_enable = getBool(profile.settings.debugEnable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.shelly.internal.manager;

import static org.openhab.binding.shelly.internal.ShellyBindingConstants.CONFIG_DEVICEIP;

import java.nio.charset.StandardCharsets;

import org.eclipse.jdt.annotation.NonNullByDefault;
Expand Down Expand Up @@ -82,6 +84,7 @@ public class ShellyManagerConstants {
public static final String ATTRIBUTE_MESSAGE = "message";
public static final String ATTRIBUTE_TOTAL_DEV = "totalDevices";
public static final String ATTRIBUTE_STATUS_ICON = "iconStatus";
public static final String ATTRIBUTE_DEVICEIP = CONFIG_DEVICEIP;
public static final String ATTRIBUTE_DISPLAY_NAME = "displayName";
public static final String ATTRIBUTE_DEV_STATUS = "deviceStatus";
public static final String ATTRIBUTE_DEBUG_MODE = "debugMode";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public ShellyMgrResponse generateContent(String path, Map<String, String[]> para
properties.put(ATTRIBUTE_FIRMWARE_SEL, "");
properties.put(ATTRIBUTE_ACTION_LIST, "");
}
if (profile.isBlu) {
properties.put(ATTRIBUTE_DISPLAY_NAME, profile.thingName);
properties.put(ATTRIBUTE_DEVICEIP, "n/a");
properties.put(PROPERTY_WIFI_NETW, "Bluetooth");
}
html += loadHTML(OVERVIEW_DEVICE, properties);
}
} catch (ShellyApiException e) {
Expand Down Expand Up @@ -193,10 +198,13 @@ private String fillFirmwareHtml(ShellyDeviceProfile profile, String uid, String
logger.debug("{}: Unable to retrieve firmware list: {}", LOG_PREFIX, e.toString());
}

html += "\t\t\t\t\t<option class=\"select-hr\" value=\"" + SHELLY_MGR_FWUPDATE_URI + "?uid=" + uid
+ "&connection=custom\">Custom URL</option>\n";

html += "\t\t\t\t</select>\n\t\t\t";
html += "\t\t\t\t\t<option class=\"select-hr\" value=\"" + SHELLY_MGR_FWUPDATE_URI + "?uid=" + uid;
if (!profile.isBlu) {
html += "&connection=custom\">Custom URL";
} else {
html += "\">Check Device App";
}
html += "</option>\n\t\t\t\t</select>\n\t\t\t";

return html;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ protected static String getDeviceType(Map<String, String> properties) {
}

protected static String getDeviceIp(Map<String, String> properties) {
return getString(properties.get("deviceIp"));
return getString(properties.get(ATTRIBUTE_DEVICEIP));
}

protected static String getDeviceName(Map<String, String> properties) {
Expand Down
Loading

0 comments on commit 8f83521

Please sign in to comment.