- 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.
| ----------------- | ------------------------------------------------------ | --------- |
| shellyblubutton | Shelly BLU Button 1 | SBBT |
| shellybludw | Shelly BLU Door/Windows | SBDW |
-
+| shellyblumotion | Shelly BLU Motion | SBMO |
## Binding Configuration
| | 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 |
// Shelly BLU
THING_TYPE_SHELLYBLUBUTTON, //
THING_TYPE_SHELLYBLUDW, //
+ THING_TYPE_SHELLYBLUMOTION, //
THING_TYPE_SHELLYPROTECTED, //
THING_TYPE_SHELLYUNKNOWN);
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);
}
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) {
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;
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) {
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;
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;
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)
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);
}
// 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";
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";
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 {
// 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);
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;
}
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;
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");
}
!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);
*/
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;
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";
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) {
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;
}
}
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) {
this.typeId = typeId;
groupLabel = getText(PREFIX_GROUP + group + ".label");
- if (groupLabel.contains(PREFIX_GROUP)) {
+ if (groupLabel.startsWith(PREFIX_GROUP)) {
groupLabel = "";
}
groupDescription = getText(PREFIX_GROUP + group + ".description");
- if (groupDescription.contains(PREFIX_GROUP)) {
+ if (groupDescription.startsWith(PREFIX_GROUP)) {
groupDescription = "";
}
label = getText(PREFIX_CHANNEL + typeId.replace(':', '.') + ".label");
- if (label.contains(PREFIX_CHANNEL)) {
+ if (label.startsWith(PREFIX_CHANNEL)) {
label = "";
}
description = getText(PREFIX_CHANNEL + typeId + ".description");
- if (description.contains(PREFIX_CHANNEL)) {
+ if (description.startsWith(PREFIX_CHANNEL)) {
description = ""; // no resource found
}
}
message.event.filtered = Event filtered: {0}
message.coap.init.failed = Unable to start CoIoT: {0}
message.discovery.disabled = Device is marked as non-discoverable, will be skipped
-message.discovery.protected = Device {0} is protected and reports 'Access denied', check userId/password
+message.discovery.protected = Device {0} is protected and reports 'Access denied', check userId/password.
message.discovery.failed = Device discovery of device with address {0} failed: {1}
message.roller.calibrating = Device is not calibrated, use Shelly App to perform initial roller calibration.
message.roller.favmissing = Roller position favorites are not supported by installed firmware or not configured in the Shelly App
# BLU devices
thing-type.shelly.shellyblubutton.description = Shelly BLU Button
thing-type.shelly.shellybludw.description = Shelly BLU Door/Window Sensor
+thing-type.shelly.shellyblumotion.description = Shelly BLU Motion Sensor
# Wall Displays
thing-type.shelly.shellywalldisplay.description = Shelly Wall Display with sensors and input/output
<config-description-ref uri="thing-type:shelly:blubattery"/>
</thing-type>
+ <thing-type id="shellyblumotion">
+ <label>Shelly BLU Motion</label>
+ <description>@text/thing-type.shelly.shellyblumotion.description</description>
+ <category>Sensor</category>
+ <channel-groups>
+ <channel-group id="sensors" typeId="sensorData"/>
+ <channel-group id="battery" typeId="batteryStatus"/>
+ <channel-group id="device" typeId="deviceStatus"/>
+ </channel-groups>
+
+ <representation-property>serviceName</representation-property>
+ <config-description-ref uri="thing-type:shelly:blubattery"/>
+ </thing-type>
</thing:thing-descriptions>
* Version 0.2
*/
-let ALLTERCO_DEVICE_NAME_PREFIX = ["SBBT", "SBDW"];
+let ALLTERCO_DEVICE_NAME_PREFIX = ["SBBT", "SBDW", "SBMO"];
let ALLTERCO_MFD_ID_STR = "0ba9";
let BTHOME_SVC_ID_STR = "fcd2";
let BTH = [];
BTH[0x00] = { n: "pid", t: uint8 };
BTH[0x01] = { n: "Battery", t: uint8, u: "%" };
+BTH[0x02] = { n: "Temperature", t: int16, f: 0.01 };
BTH[0x05] = { n: "Illuminance", t: uint24, f: 0.01 };
BTH[0x1a] = { n: "Door", t: uint8 };
BTH[0x20] = { n: "Moisture", t: uint8 };
+BTH[0x21] = { n: "Motion", t: uint8 };
BTH[0x2d] = { n: "Window", t: uint8 };
BTH[0x3a] = { n: "Button", t: uint8 };
BTH[0x3f] = { n: "Rotation", t: int16, f: 0.1 };