Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
663 changes: 388 additions & 275 deletions bundles/org.openhab.binding.shelly/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ public class ShellyBindingConstants {
public static final String CHANNEL_TIMER_AUTOON = "autoOn";
public static final String CHANNEL_TIMER_AUTOOFF = "autoOff";
public static final String CHANNEL_TIMER_ACTIVE = "timerActive";
public static final String CHANNEL_DALI_DEVICES = "daliDeviceCount";
public static final String CHANNEL_DALI_SCAN_ACTIVE = "daliScanActive";

// Roller
public static final String CHANNEL_GROUP_ROL_CONTROL = "roller";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,16 @@ public class ShellyDevices {
public static final String SHELLYDT_PLUSSMOKE = "SNSN-0031Z";
public static final String SHELLYDT_PLUSFLOODG4 = "S4SN-0071A"; // Shelly Flood Gen4
public static final String SHELLYDT_PLUSUNI = "SNSN-0043X";
public static final String SHELLYDT_PLUSDIMMEREU = "SNDM-0011EU";
public static final String SHELLYDT_PLUSDIMMERUS = "SNDM-0013US";
public static final String SHELLYDT_PLUSDIMMERG3 = "S3DM-0A101WWL";
public static final String SHELLYDT_PLUSDIMMER10V = "SNDM-00100WW";
public static final String SHELLYDT_PLUSDIMMER0110VG3 = "S3DM-0010WW";
public static final String SHELLYDT_PRODM2PM = "SPDM-002PE01EU";
public static final String SHELLYDT_PLUSDIMMERG4 = "S4DM-0A101WWL";
public static final String SHELLYDT_PLUSDIMMER10V = "SNDM-00100WW"; // Gen2: 0-10V
public static final String SHELLYDT_PLUSDIMMER10V_2 = "SNGW-0A11WW010"; // Gen2: alt SKU
public static final String SHELLYDT_PLUSDIMMER0110VG3 = "S3DM-0010WW"; // Gen3; 0-1/10V
public static final String SHELLYDT_PLUSDIMMERDALIG3 = "S3DM-0A1WW";
public static final String SHELLYDT_PLUSDIMMER0110VG4 = "S4DM-0010WW"; // Gen4; 0-1/10V
public static final String SHELLYDT_PLUSDIMMERDALIG4 = "S4DM-0A1WW";
public static final String SHELLYDT_PLUSWALLDISPLAY = "SAWD-0A1XX10EU1";
public static final String SHELLYDT_PLUSRGBWPM = "SNDC-0D4P10WW";
public static final String SHELLYDT_PLUSSTRIP = "S4PL-00416EU";
Expand Down Expand Up @@ -144,6 +149,9 @@ public class ShellyDevices {
public static final String SHELLYDT_PRO3EM_2 = "SPEM-003CEBEU120";
public static final String SHELLYDT_PRO3EM3CT63 = "SPEM-003CEBEU63";
public static final String SHELLYDT_PRO3EM400 = "SPEM-003CEBEU400";
public static final String SHELLYDT_PRODIMMER1PM = "SPDM-001PE01EU";
public static final String SHELLYDT_PRODIMMER2PM = "SPDM-002PE01EU";
public static final String SHELLYDT_PRODIMMER10V = "SPCC-001PE10EU";

// Shelly Plus Mini Series
public static final String SHELLYDT_MINI_1 = "SNSW-001X8EU";
Expand Down Expand Up @@ -260,7 +268,8 @@ public class ShellyDevices {
public static final ThingTypeUID THING_TYPE_SHELLYPLUSDIMMERUS = new ThingTypeUID(BINDING_ID, "shellypluswdus");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSDIMMER10V = new ThingTypeUID(BINDING_ID, "shellyplus10v");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSDIMMER = new ThingTypeUID(BINDING_ID, "shellyplusdimmer");
public static final ThingTypeUID THING_TYPE_SHELLYPRODM2PM = new ThingTypeUID(BINDING_ID, "shellyprodm2pm");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSDALIDIMMER = new ThingTypeUID(BINDING_ID,
"shellyplusdalidimmer");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSRGBWPM = new ThingTypeUID(BINDING_ID, "shellyplusrgbwpm");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSSTRIP = new ThingTypeUID(BINDING_ID, "shellyplusstrip");
public static final ThingTypeUID THING_TYPE_SHELLYPLUSEM = new ThingTypeUID(BINDING_ID, "shellyplusem");
Expand Down Expand Up @@ -291,6 +300,9 @@ public class ShellyDevices {
public static final ThingTypeUID THING_TYPE_SHELLYPRO3EM = new ThingTypeUID(BINDING_ID, "shellypro3em");
public static final ThingTypeUID THING_TYPE_SHELLYPRO3EM63 = new ThingTypeUID(BINDING_ID, "shellypro3em63");
public static final ThingTypeUID THING_TYPE_SHELLYPRO3EM400 = new ThingTypeUID(BINDING_ID, "shellypro3em400");
public static final ThingTypeUID THING_TYPE_SHELLYPRODIMMER1PM = new ThingTypeUID(BINDING_ID, "shellyprodimmer1pm");
public static final ThingTypeUID THING_TYPE_SHELLYPRODIMMER2PM = new ThingTypeUID(BINDING_ID, "shellyprodm2pm");
public static final ThingTypeUID THING_TYPE_SHELLYPRODIMMER10V = new ThingTypeUID(BINDING_ID, "shellyprodimmer10v");

// Shelly Blu series
public static final ThingTypeUID THING_TYPE_SHELLYBLUBUTTON1 = new ThingTypeUID(BINDING_ID, "shellyblubutton");
Expand All @@ -313,7 +325,8 @@ public class ShellyDevices {
public static final Set<ThingTypeUID> GROUP_DIMMER_THING_TYPES = Set.of( //
THING_TYPE_SHELLYDIMMER, THING_TYPE_SHELLYDIMMER2, // Gen 1
THING_TYPE_SHELLYPLUSDIMMER, THING_TYPE_SHELLYPLUSDIMMERUS, THING_TYPE_SHELLYPLUSDIMMER10V, // Plus
THING_TYPE_SHELLYPRODM2PM); // Pro
THING_TYPE_SHELLYPLUSDALIDIMMER, // Plus DALI
THING_TYPE_SHELLYPRODIMMER1PM, THING_TYPE_SHELLYPRODIMMER2PM, THING_TYPE_SHELLYPRODIMMER10V); // Pro

// Duo devices
public static final Set<ThingTypeUID> GROUP_DUO_THING_TYPES = Set.of( //
Expand Down Expand Up @@ -461,11 +474,16 @@ public class ShellyDevices {
Map.entry(SHELLYDT_PLUSSMOKE, THING_TYPE_SHELLYPLUSSMOKE),
Map.entry(SHELLYDT_PLUSFLOODG4, THING_TYPE_SHELLYPLUSFLOOD),
Map.entry(SHELLYDT_PLUSUNI, THING_TYPE_SHELLYPLUSUNI),
Map.entry(SHELLYDT_PLUSDIMMEREU, THING_TYPE_SHELLYPLUSDIMMER),
Map.entry(SHELLYDT_PLUSDIMMERUS, THING_TYPE_SHELLYPLUSDIMMERUS),
Map.entry(SHELLYDT_PLUSDIMMER10V, THING_TYPE_SHELLYPLUSDIMMER10V),
Map.entry(SHELLYDT_PLUSDIMMER10V_2, THING_TYPE_SHELLYPLUSDIMMER10V),
Map.entry(SHELLYDT_PLUSDIMMER0110VG3, THING_TYPE_SHELLYPLUSDIMMER10V),
Map.entry(SHELLYDT_PLUSDIMMER0110VG4, THING_TYPE_SHELLYPLUSDIMMER10V),
Map.entry(SHELLYDT_PLUSDIMMERG3, THING_TYPE_SHELLYPLUSDIMMER),
Map.entry(SHELLYDT_PRODM2PM, THING_TYPE_SHELLYPRODM2PM),
Map.entry(SHELLYDT_PLUSDIMMERG4, THING_TYPE_SHELLYPLUSDIMMER),
Map.entry(SHELLYDT_PLUSDIMMERDALIG3, THING_TYPE_SHELLYPLUSDALIDIMMER),
Map.entry(SHELLYDT_PLUSDIMMERDALIG4, THING_TYPE_SHELLYPLUSDALIDIMMER),
Map.entry(SHELLYDT_PLUSRGBWPM, THING_TYPE_SHELLYPLUSRGBWPM),
Map.entry(SHELLYDT_PLUSEM, THING_TYPE_SHELLYPLUSEM),
Map.entry(SHELLYDT_PLUS3EM63, THING_TYPE_SHELLYPLUS3EM63),
Expand Down Expand Up @@ -505,6 +523,9 @@ public class ShellyDevices {
Map.entry(SHELLYDT_PRO3EM_2, THING_TYPE_SHELLYPRO3EM), //
Map.entry(SHELLYDT_PRO3EM3CT63, THING_TYPE_SHELLYPRO3EM63), //
Map.entry(SHELLYDT_PRO3EM400, THING_TYPE_SHELLYPRO3EM400), //
Map.entry(SHELLYDT_PRODIMMER1PM, THING_TYPE_SHELLYPRODIMMER1PM), //
Map.entry(SHELLYDT_PRODIMMER2PM, THING_TYPE_SHELLYPRODIMMER2PM), //
Map.entry(SHELLYDT_PRODIMMER10V, THING_TYPE_SHELLYPRODIMMER10V), //

// BLU Series
Map.entry(SHELLYDT_BLUBUTTON1, THING_TYPE_SHELLYBLUBUTTON1),
Expand Down Expand Up @@ -654,7 +675,12 @@ public class ShellyDevices {
Map.entry("shellyplus10v", THING_TYPE_SHELLYPLUSDIMMER10V),
Map.entry("shellyplusdimmer", THING_TYPE_SHELLYPLUSDIMMER),
Map.entry("shellydimmerg3", THING_TYPE_SHELLYPLUSDIMMER),
Map.entry("shellyprodm2pm", THING_TYPE_SHELLYPRODM2PM),
Map.entry("shellydimmerg4", THING_TYPE_SHELLYPLUSDIMMER),
Map.entry("shellyddimmerg3", THING_TYPE_SHELLYPLUSDALIDIMMER),
Map.entry("shellyddimmerg4", THING_TYPE_SHELLYPLUSDALIDIMMER),
Map.entry("shellyplus010v", THING_TYPE_SHELLYPLUSDIMMER10V), // Gen2 alt
Map.entry("shelly0110dimg3", THING_TYPE_SHELLYPLUSDIMMER10V), // Gen3
Map.entry("shelly0110dimg4", THING_TYPE_SHELLYPLUSDIMMER10V), // Gen4
Map.entry("shellyplusrgbwpm", THING_TYPE_SHELLYPLUSRGBWPM),
Map.entry("shellyplusstrip", THING_TYPE_SHELLYPLUSSTRIP),
Map.entry("shellyblugw", THING_TYPE_SHELLYPLUSBLUGW), //
Expand Down Expand Up @@ -693,6 +719,12 @@ public class ShellyDevices {
Map.entry("shellypro3em63", THING_TYPE_SHELLYPRO3EM63), //
Map.entry("shellypro3em3ct63", THING_TYPE_SHELLYPRO3EM63), //
Map.entry("shellypro3em400", THING_TYPE_SHELLYPRO3EM400),
Map.entry("shellyprodimmer1pm", THING_TYPE_SHELLYPRODIMMER1PM),
Map.entry("shellyprodm1pm", THING_TYPE_SHELLYPRODIMMER1PM),
Map.entry("shellyprodimmer2pm", THING_TYPE_SHELLYPRODIMMER2PM),
Map.entry("shellyprodm2pm", THING_TYPE_SHELLYPRODIMMER2PM),
Map.entry("shellyprodimmer10v", THING_TYPE_SHELLYPRODIMMER10V),
Map.entry("shellypro0110pm", THING_TYPE_SHELLYPRODIMMER10V),

// Shelly BLU Series
Map.entry("shellyblubutton", THING_TYPE_SHELLYBLUBUTTON1), //
Expand Down Expand Up @@ -723,7 +755,14 @@ public class ShellyDevices {
Map.entry(THING_TYPE_SHELLYPLUS3EM63, 3), //
Map.entry(THING_TYPE_SHELLYPRO3EM, 3), //
Map.entry(THING_TYPE_SHELLYPRO3EM63, 3), //
Map.entry(THING_TYPE_SHELLYPRO3EM400, 3));
Map.entry(THING_TYPE_SHELLYPRO3EM400, 3), //
Map.entry(THING_TYPE_SHELLYPRODIMMER1PM, 1), //
Map.entry(THING_TYPE_SHELLYPRODIMMER2PM, 2), //
Map.entry(THING_TYPE_SHELLYPRODIMMER10V, 1), //
Map.entry(THING_TYPE_SHELLYPLUSDIMMER, 1), //
Map.entry(THING_TYPE_SHELLYPLUSDIMMERUS, 1), //
Map.entry(THING_TYPE_SHELLYPLUSDIMMER10V, 1), //
Map.entry(THING_TYPE_SHELLYPLUSDALIDIMMER, 1));

// Number of inputs
public static final Map<ThingTypeUID, Integer> THING_TYPE_CAP_NUM_INPUTS = Map.ofEntries( //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,12 @@ public boolean isHttpServerError() {
return httpCode == INTERNAL_SERVER_ERROR_500;
}

public boolean isNotCalibrtated() {
return response.contains(SHELLY_APIERR_NOT_CALIBRATED);
/**
* Returns {@code true} when the device response indicates that a calibration run is required.
* Covers Gen1 rollers ("Not calibrated!") and Gen2 Pro Dimmers ("Output not calibrated!").
*/
public boolean isNotCalibrated() {
return response.toLowerCase(Locale.ROOT).contains("not calibrated");
}

public ShellyApiResultBuilder modify() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,19 +358,42 @@ public String getInputGroup(int i) {
return CHANNEL_GROUP_STATUS;
} else if (isRoller) {
return numRelays <= 2 ? CHANNEL_GROUP_ROL_CONTROL : CHANNEL_GROUP_ROL_CONTROL + idx;
} else {
// Device has 1 input per relay: 0=off, 1+2 depend on switch mode
return numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL : CHANNEL_GROUP_RELAY_CONTROL + idx;
} else if (isDimmer) {
List<ShellySettingsDimmer> dimmers = settings.dimmers;
if (dimmers != null) {
int numDimmer = dimmers.size();
if (numDimmer <= 1) {
return CHANNEL_GROUP_RELAY_CONTROL;
}
int numChannels = numInputs / numDimmer; // Device has more than 1 dimmer
if (numChannels == 0) {
return CHANNEL_GROUP_RELAY_CONTROL;
}
return CHANNEL_GROUP_RELAY_CONTROL + (1 + (i / numChannels));
}
}
// Device has 1 input per relay: 0=off, 1+2 depend on switch mode
return numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL : CHANNEL_GROUP_RELAY_CONTROL + idx;
}

public String getInputSuffix(int i) {
int idx = i + 1; // channel names are 1-based
if (isRGBW2 || isIX || isMultiButton) {
return ""; // RGBW2 has only 1 channel
} else if (isRoller || isDimmer) {
} else if (isRoller) {
// Roller has 2 relays, but it will be mapped to 1 roller with 2 inputs
// Dimmer has up to 2 inputs to control light
return String.valueOf(idx);
} else if (isDimmer) {
List<ShellySettingsDimmer> dimmers = settings.dimmers;
if (dimmers != null) {
int numDimmer = dimmers.size();
if (numDimmer <= 1) {
return String.valueOf(idx);
}
int numChannels = numInputs / numDimmer; // inputs per dimmer channel, same as getInputGroup()
return String.valueOf(numChannels == 0 ? idx : (i % numChannels) + 1);
}
// Dimmer has up to 2 inputs to control light (Gen1 fallback, no dimmers list)
return String.valueOf(idx);
} else if (hasRelays) {
return numRelays == 1 && numInputs >= 2 ? String.valueOf(idx) : "";
Expand All @@ -394,8 +417,10 @@ public boolean inButtonMode(int idx) {
ShellySettingsInput input = inputs.get(idx);
btnType = getString(input.btnType);
} else if (isDimmer) {
if (dimmers != null) {
ShellySettingsDimmer dimmer = dimmers.get(0);
if (isGen2 && inputs != null && idx < inputs.size()) {
btnType = getString(inputs.get(idx).btnType);
} else if (dimmers != null && !dimmers.isEmpty()) {
ShellySettingsDimmer dimmer = dimmers.get(Math.min(idx, dimmers.size() - 1));
btnType = getString(dimmer.btnType);
}
} else if (relays != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyMotionSettings;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2APClientList;
Expand Down Expand Up @@ -154,7 +155,6 @@ public class Shelly1ApiJsonDTO {
// API Error Codes
public static final String SHELLY_APIERR_UNAUTHORIZED = "Unauthorized";
public static final String SHELLY_APIERR_TIMEOUT = "Timeout";
public static final String SHELLY_APIERR_NOT_CALIBRATED = "Not calibrated!";

// API device types / properties
public static final String SHELLY_CLASS_RELAY = "relay"; // Relay: relay mode
Expand Down Expand Up @@ -656,7 +656,7 @@ public static class ShellySettingsGlobal {

public @Nullable ArrayList<ShellySettingsRelay> relays;
public @Nullable ArrayList<ShellySettingsInput> inputs; // ix3
public @Nullable ArrayList<ShellySettingsDimmer> dimmers;
public @Nullable ArrayList<@NonNull ShellySettingsDimmer> dimmers;
public @Nullable ArrayList<ShellySettingsRoller> rollers;
public @Nullable ArrayList<ShellySettingsRgbwLight> lights;
public @Nullable ArrayList<ShellySettingsEMeter> emeters;
Expand Down Expand Up @@ -784,7 +784,9 @@ public static class ShellySettingsStatus {
public Double voltage; // Shelly 2.5
public Integer input; // RGBW2 has no JSON array
public ArrayList<ShellyInputState> inputs;
public ArrayList<ShellyShortLightStatus> dimmers;
public @Nullable ArrayList<@NonNull ShellyShortLightStatus> dimmers;
public @Nullable Integer daliCgCount; // Gen2 DALI Dimmer: control gear count on the DALI bus
public @Nullable Boolean daliScanActive; // Gen2 DALI Dimmer: a bus scan is currently in progress
public ArrayList<ShellyRollerStatus> rollers;
public ArrayList<ShellySettingsLight> lights;
public ArrayList<ShellySettingsMeter> meters;
Expand Down
Loading