[shelly] Add support for Shelly Wall Dimmer US and Wall Display (#15051)

Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
Markus Michels 2023-07-08 17:05:49 +02:00 committed by GitHub
parent 5629d75d60
commit 5bea9bcfdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 663 additions and 285 deletions

View File

@ -90,6 +90,8 @@ The binding provides the same feature set across all devices as good as possible
| shellyplusi4dc | Shelly Plus i4 with 4x DC input | SNSN-0D24X |
| shellyplusht | Shelly Plus HT with temperature + humidity sensor | SNSN-0013A |
| shellyplussmoke | Shelly Plus Smoke sensor | SNSN-0031Z |
| shellypluswdus | Shelly Plus Wall Dimmer US | SNDM-0013US |
| shellypluswalldisplay| Shelly Plus Wall Display | SAWD-0A1XX10EU1 |
### Generation 2 Pro series
@ -175,6 +177,7 @@ The binding can't communicate directly with the device, but the Plus/Pro series
The binding automatically installs a script on the Shelly Device (oh-blu-scanner), which forwards the BLU events to the binding using the WebSocket channel.
Follow these steps to add the Shelly BLU Device to openHAB
- Make sure a Shelly is near by the BLU device, enable Bluetooh on this device (the Bluetooth Gateway mode is not required)
- Add this thing to openHAB, make sure thing gets online
- Enable "BLU Gateway Support" in the thing configuration of the Shelly device acting as gateway.
@ -1202,6 +1205,16 @@ Channels lastEvent and eventCount are only available if input type is set to mom
| battery | batteryLevel | Number | yes | Battery Level in % |
| | lowBattery | Switch | yes | Low battery alert (< 20%) |
### Shelly Plus Wall Dimmer US (thing-type: shellypluswdus)
|Group | Channel |Type |read-only |Description |
|-------|-------------|---------|----------|------------------------------------------------------------------------------------|
| relay | brightness | Dimmer | r/w | Currently selected brightness. |
| | outputName | String | yes | Logical name of this relay output as configured in the Shelly App |
| | autoOn | Number | r/w | Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds |
| | autoOff | Number | r/w | Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds |
| | timerActive | Switch | yes | Relay #1: ON: An auto-on/off timer is active |
|
## Shelly Pro Series
@ -1405,6 +1418,22 @@ 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 Wall Displays
| Group | Channel | Type | read-only | Description |
| ------- | ----------- | -------- | --------- | --------------------------------------------------------------------------------- |
| relay | output | Switch | r/w | Controls the relay's output channel (on/off) |
| | outputName | String | yes | Logical name of this relay output as configured in the Shelly App |
| | input | Switch | yes | ON: Input/Button is powered, see General Notes on Channels |
| | autoOn | Number | r/w | Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds |
| | autoOff | Number | r/w | Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds |
| | timerActive | Switch | yes | Relay #1: ON: An auto-on/off timer is active |
| | button | Trigger | yes | Event trigger, see section Button Events |
| sensors | temperature | Number | yes | Temperature reported by the integrated sensor |
| | humidity | Number | yes | Relative Humidity in percent reported by the integrated sensor |
| | lux | Number | yes | Brightness in Lux reported by the integrated sensor |
| | lastUpdate | DateTime | yes | Timestamp of the last update (any sensor value changed) |
## Full Example
### shelly.things

View File

@ -67,10 +67,22 @@ public class ShellyBindingConstants {
THING_TYPE_SHELLYBUTTON1, //
THING_TYPE_SHELLYBUTTON2, //
THING_TYPE_SHELLMOTION, //
// Shelly Plus
THING_TYPE_SHELLYPLUS1, //
THING_TYPE_SHELLYPLUS1PM, //
THING_TYPE_SHELLYPLUS2PM_RELAY, //
THING_TYPE_SHELLYPLUS2PM_ROLLER, //
THING_TYPE_SHELLYPLUSI4, //
THING_TYPE_SHELLYPLUSI4DC, //
THING_TYPE_SHELLYPLUSHT, //
THING_TYPE_SHELLYPLUSSMOKE, //
THING_TYPE_SHELLYPLUSPLUGS, //
THING_TYPE_SHELLYPLUSPLUGUS, //
THING_TYPE_SHELLYPLUSDIMMERUS, //
THING_TYPE_SHELLYPLUSWALLDISPLAY, //
// Shelly Pro
THING_TYPE_SHELLYPRO1, //
THING_TYPE_SHELLYPRO1PM, //
THING_TYPE_SHELLYPRO2_RELAY, //
@ -79,14 +91,11 @@ public class ShellyBindingConstants {
THING_TYPE_SHELLYPRO3, //
THING_TYPE_SHELLYPRO3EM, //
THING_TYPE_SHELLYPRO4PM, //
THING_TYPE_SHELLYPLUSI4, //
THING_TYPE_SHELLYPLUSI4DC, //
THING_TYPE_SHELLYPLUSHT, //
THING_TYPE_SHELLYPLUSSMOKE, //
THING_TYPE_SHELLYPLUSPLUGS, //
THING_TYPE_SHELLYPLUSPLUGUS, //
// Shelly BLU
THING_TYPE_SHELLYBLUBUTTON, //
THING_TYPE_SHELLYBLUDW, //
THING_TYPE_SHELLYPROTECTED, //
THING_TYPE_SHELLYUNKNOWN);

View File

@ -102,6 +102,8 @@ public class ShellyDeviceProfile {
public boolean isIX = false; // true for a Shelly IX
public boolean isTRV = false; // true for a Shelly TRV
public boolean isSmoke = false; // true for Shelly Smoke
public boolean isWall = false; // true: Shelly Wall Display
public boolean is3EM = false; // true for Shelly 3EM and Pro 3EM
public int minTemp = 0; // Bulb/Duo: Min Light Temp
public int maxTemp = 0; // Bulb/Duo: Max Light Temp
@ -138,9 +140,8 @@ public class ShellyDeviceProfile {
name = getString(settings.name);
deviceType = getString(settings.device.type);
mac = getString(settings.device.mac);
hostname = settings.device.hostname != null && !settings.device.hostname.isEmpty()
? settings.device.hostname.toLowerCase()
: "shelly-" + mac.toUpperCase().substring(6, 11);
hostname = !getString(settings.device.hostname).isEmpty() ? settings.device.hostname.toLowerCase()
: mac.length() >= 12 ? "shelly-" + mac.toUpperCase().substring(6, 11) : "unknown";
mode = getString(settings.mode).toLowerCase();
hwRev = settings.hwinfo != null ? getString(settings.hwinfo.hwRevision) : "";
hwBatchId = settings.hwinfo != null ? getString(settings.hwinfo.batchId.toString()) : "";
@ -193,7 +194,9 @@ public class ShellyDeviceProfile {
isBlu = thingType.startsWith("shellyblu"); // e.g. SBBT for BU Button
isDimmer = deviceType.equalsIgnoreCase(SHELLYDT_DIMMER) || deviceType.equalsIgnoreCase(SHELLYDT_DIMMER2);
isDimmer = deviceType.equalsIgnoreCase(SHELLYDT_DIMMER) || deviceType.equalsIgnoreCase(SHELLYDT_DIMMER2)
|| deviceType.equalsIgnoreCase(SHELLYDT_PLUSDIMMERUS)
|| thingType.equalsIgnoreCase(THING_TYPE_SHELLYPLUSDIMMERUS_STR);
isBulb = thingType.equals(THING_TYPE_SHELLYBULB_STR);
isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)
|| thingType.equals(THING_TYPE_SHELLYDUORGBW_STR);
@ -217,10 +220,13 @@ public class ShellyDeviceProfile {
|| thingType.equals(THING_TYPE_SHELLYPLUSI4DC_STR);
isButton = thingType.equals(THING_TYPE_SHELLYBUTTON1_STR) || thingType.equals(THING_TYPE_SHELLYBUTTON2_STR)
|| thingType.equals(THING_TYPE_SHELLYBLUBUTTON_STR);
isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isUNI || isMotion || isSense || isTRV;
hasBattery = isHT || isFlood || isDW || isSmoke || isButton || isMotion || isTRV;
isTRV = thingType.equals(THING_TYPE_SHELLYTRV_STR);
isWall = thingType.equals(THING_TYPE_SHELLYPLUSWALLDISPLAY_STR);
is3EM = thingType.equals(THING_TYPE_SHELLY3EM_STR) || thingType.equals(THING_TYPE_SHELLYPRO3EM_STR);
isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isUNI || isMotion || isSense || isTRV
|| isWall;
hasBattery = isHT || isFlood || isDW || isSmoke || isButton || isMotion || isTRV;
alwaysOn = !hasBattery || isMotion || isSense; // true means: device is reachable all the time (no sleep mode)
}

View File

@ -405,6 +405,7 @@ public class Shelly1ApiJsonDTO {
public String pushShortUrl; // to access when roller stopped
// Status information
public Integer id;
public Boolean ison;
public Boolean overpower;
@SerializedName("is_valid")
@ -756,7 +757,7 @@ public class Shelly1ApiJsonDTO {
public ShellyStatusSensor.ShellyExtSwitchStatus extSwitch;
// Internal device temp
public ShellySensorTmp tmp = new ShellySensorTmp(); // Shelly 1PM
public ShellySensorTmp tmp; // Shelly 1PM
public Double temperature; // Shelly 2.5
public Boolean overtemperature;
@ -825,6 +826,8 @@ public class Shelly1ApiJsonDTO {
public Integer brightness; // brightness: 0.100%
@SerializedName("has_timer")
public Boolean hasTimer;
@SerializedName("timer_duration")
public Integer timerDuration;
}
public static class ShellyStatusRelay {

View File

@ -149,7 +149,7 @@ public class Shelly1CoIoTVersion1 extends Shelly1CoIoTProtocol implements Shelly
break;
case "current":
updateChannel(updates, rGroup, CHANNEL_EMETER_CURRENT,
toQuantityType(getDouble(s.value), DIGITS_VOLT, Units.AMPERE));
toQuantityType(getDouble(s.value), DIGITS_AMPERE, Units.AMPERE));
break;
case "pf":
updateChannel(updates, rGroup, CHANNEL_EMETER_PFACTOR, getDecimal(s.value));

View File

@ -90,7 +90,9 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
@Override
public ShellySettingsDevice getDeviceInfo() throws ShellyApiException {
return callApi(SHELLY_URL_DEVINFO, ShellySettingsDevice.class);
ShellySettingsDevice info = callApi(SHELLY_URL_DEVINFO, ShellySettingsDevice.class);
info.gen = 1;
return info;
}
@Override

View File

@ -32,12 +32,14 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyFavPos;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyInputState;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySensorTmp;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsEMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsInput;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRoller;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor;
@ -49,16 +51,19 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSe
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyExtVoltage;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellySensorBat;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellySensorHum;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellySensorLux;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2AuthRequest;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2AuthResponse;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfig.Shelly2DevConfigCover;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfig.Shelly2DevConfigInput;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfig.Shelly2DevConfigSwitch;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfig.Shelly2GetConfigResult;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusLight;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2CoverStatus;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusEm;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusHumidity;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusIlluminance;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusPower;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusSmoke;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusTempId;
@ -164,6 +169,8 @@ public class Shelly2ApiClient extends ShellyHttpClient {
}
ShellySettingsRelay rsettings = new ShellySettingsRelay();
rsettings.id = cs.id;
rsettings.isValid = cs.id != null;
rsettings.name = cs.name;
rsettings.ison = false;
rsettings.autoOn = getBool(cs.autoOn) ? cs.autoOnDelay : 0;
@ -177,7 +184,10 @@ public class Shelly2ApiClient extends ShellyHttpClient {
boolean channelUpdate) throws ShellyApiException {
boolean updated = false;
if (result.temperature0 != null && !getProfile().isSensor) {
if (result.temperature0 != null && result.temperature0.tC != null && !getProfile().isSensor) {
if (status.tmp == null) {
status.tmp = new ShellySensorTmp();
}
status.temperature = status.tmp.tC = result.temperature0.tC;
}
@ -188,12 +198,14 @@ public class Shelly2ApiClient extends ShellyHttpClient {
updated |= updateRelayStatus(status, result.switch3, channelUpdate);
updated |= updateEmStatus(status, result.em0, channelUpdate);
updated |= updateRollerStatus(status, result.cover0, channelUpdate);
updated |= updateDimmerStatus(status, result.light0, channelUpdate);
if (channelUpdate) {
updated |= ShellyComponents.updateMeters(getThing(), status);
}
updateHumidityStatus(sensorData, result.humidity0);
updateTemperatureStatus(sensorData, result.temperature0);
updateIlluminanceStatus(sensorData, result.illuminance0);
updateSmokeStatus(sensorData, result.smoke0);
updateBatteryStatus(sensorData, result.devicepower0);
updateAddonStatus(status, result);
@ -222,7 +234,13 @@ public class Shelly2ApiClient extends ShellyHttpClient {
int duration = (int) (now() - rs.timerStartetAt);
sr.timerRemaining = duration;
}
if (status.tmp == null) {
status.tmp = new ShellySensorTmp();
}
if (rs.temperature != null) {
if (status.tmp == null) {
status.tmp = new ShellySensorTmp();
}
status.tmp.isValid = true;
status.tmp.tC = rs.temperature.tC;
status.tmp.tF = rs.temperature.tF;
@ -231,8 +249,6 @@ public class Shelly2ApiClient extends ShellyHttpClient {
if (status.temperature == null || getDouble(rs.temperature.tC) > status.temperature) {
status.temperature = sr.temperature;
}
} else {
status.tmp.isValid = false;
}
if (rs.voltage != null) {
if (status.voltage == null || rs.voltage > status.voltage) {
@ -250,11 +266,11 @@ public class Shelly2ApiClient extends ShellyHttpClient {
ShellySettingsMeter sm = new ShellySettingsMeter();
ShellySettingsEMeter emeter = status.emeters.get(rs.id);
sm.isValid = emeter.isValid = true;
if (rs.apower != null) {
sm.power = emeter.power = rs.apower;
}
if (rs.aenergy != null) {
// Gen2 reports Watt, needs to be converted to W/h
sm.total = emeter.total = rs.aenergy.total;
sm.counters = rs.aenergy.byMinute;
sm.timestamp = rs.aenergy.minuteTs;
@ -278,6 +294,12 @@ public class Shelly2ApiClient extends ShellyHttpClient {
private void updateMeter(ShellySettingsStatus status, int id, ShellySettingsMeter sm, ShellySettingsEMeter emeter,
boolean channelUpdate) throws ShellyApiException {
if (getProfile().numMeters == 0) {
return;
}
sm.isValid = sm.power != null || sm.total != null;
emeter.isValid = emeter.current != null || emeter.voltage != null || emeter.power != null;
emeter.isValid = emeter.current != null || emeter.voltage != null || emeter.power != null;
status.meters.set(id, sm);
status.emeters.set(id, emeter);
relayStatus.meters.set(id, sm);
@ -301,7 +323,6 @@ public class Shelly2ApiClient extends ShellyHttpClient {
ShellySettingsMeter sm = new ShellySettingsMeter();
ShellySettingsEMeter emeter = status.emeters.get(0);
sm.isValid = emeter.isValid = true;
if (em.aActPower != null) {
sm.power = emeter.power = em.aActPower;
}
@ -322,7 +343,6 @@ public class Shelly2ApiClient extends ShellyHttpClient {
sm = new ShellySettingsMeter();
emeter = status.emeters.get(1);
sm.isValid = emeter.isValid = true;
if (em.bActPower != null) {
sm.power = emeter.power = em.bActPower;
}
@ -446,6 +466,9 @@ public class Shelly2ApiClient extends ShellyHttpClient {
rs.duration = (int) (now() - cs.moveStartedAt.longValue());
}
if (cs.temperature != null && cs.temperature.tC > getDouble(status.temperature)) {
if (status.tmp == null) {
status.tmp = new ShellySensorTmp();
}
status.temperature = status.tmp.tC = getDouble(cs.temperature.tC);
}
if (cs.apower != null) {
@ -482,6 +505,46 @@ public class Shelly2ApiClient extends ShellyHttpClient {
return updateChannels ? ShellyComponents.updateRoller((ShellyBaseHandler) getThing(), rs, cs.id) : false;
}
protected void fillDimmerSettings(ShellyDeviceProfile profile, Shelly2GetConfigResult dc) {
if (!profile.isDimmer || dc.light0 == null) {
return;
}
if (profile.settings.dimmers != null) {
ShellySettingsDimmer ds = profile.settings.dimmers.get(0);
ds.autoOn = dc.light0.autoOnDelay;
ds.autoOff = dc.light0.autoOffDelay;
ds.name = dc.light0.name;
profile.settings.dimmers.set(0, ds);
}
}
private boolean updateDimmerStatus(ShellySettingsStatus status, @Nullable Shelly2DeviceStatusLight value,
boolean channelUpdate) throws ShellyApiException {
ShellyDeviceProfile profile = getProfile();
if (!profile.isDimmer || value == null) {
return false;
}
ShellyShortLightStatus ds = status.dimmers.get(0);
if (value.brightness != null) {
ds.brightness = value.brightness.intValue();
}
ds.ison = value.output;
ds.hasTimer = value.timerStartedAt != null;
ds.timerDuration = getDuration(value.timerStartedAt, value.timerDuration);
status.dimmers.set(0, ds);
return channelUpdate ? ShellyComponents.updateDimmers(getThing(), status) : false;
}
protected @Nullable Integer getDuration(@Nullable Double timerStartedAt, @Nullable Integer timerDuration) {
if (timerStartedAt == null || timerDuration == null) {
return null;
}
int duration = (int) (now() - timerStartedAt.longValue());
return duration <= timerDuration ? timerDuration - duration : 0;
}
// Addon
private void updateAddonStatus(ShellySettingsStatus status, @Nullable Shelly2DeviceStatusResult ds)
throws ShellyApiException {
@ -544,6 +607,18 @@ public class Shelly2ApiClient extends ShellyHttpClient {
sdata.tmp.tF = value.tF;
}
protected void updateIlluminanceStatus(ShellyStatusSensor sdata, @Nullable Shelly2DeviceStatusIlluminance value) {
if (value == null) {
return;
}
if (sdata.lux == null) {
sdata.lux = new ShellySensorLux();
}
sdata.lux.isValid = value.lux != null;
sdata.lux.value = value.lux;
sdata.lux.illumination = value.illumination;
}
protected void updateSmokeStatus(ShellyStatusSensor sdata, @Nullable Shelly2DeviceStatusSmoke value) {
if (value == null) {
return;

View File

@ -47,6 +47,10 @@ public class Shelly2ApiJsonDTO {
public static final String SHELLY2_COVER_CMD_OPEN = "Open";
public static final String SHELLY2_COVER_CMD_CLOSE = "Close";
public static final String SHELLY2_COVER_CMD_STOP = "Stop";
public static final String SHELLYRPC_METHOD_LIGHT_STATUS = "Light.GetStatus";
public static final String SHELLYRPC_METHOD_LIGHT_SET = "Light.Set";
public static final String SHELLYRPC_METHOD_LIGHT_SETCONFIG = "Light.SetConfig";
public static final String SHELLYRPC_METHOD_LED_SETCONFIG = "WD_UI.SetConfig";
public static final String SHELLYRPC_METHOD_WIFIGETCONG = "Wifi.GetConfig";
public static final String SHELLYRPC_METHOD_WIFISETCONG = "Wifi.SetConfig";
public static final String SHELLYRPC_METHOD_ETHGETCONG = "Eth.GetConfig";
@ -56,6 +60,7 @@ public class Shelly2ApiJsonDTO {
public static final String SHELLYRPC_METHOD_CLOUDSET = "Cloud.SetConfig";
public static final String SHELLYRPC_METHOD_WSGETCONFIG = "WS.GetConfig";
public static final String SHELLYRPC_METHOD_WSSETCONFIG = "WS.SetConfig";
public static final String SHELLYRPC_METHOD_EMDATARESET = "EMData.DeleteAllData";
public static final String SHELLYRPC_METHOD_SMOKE_SETCONFIG = "Smoke.SetConfig";
public static final String SHELLYRPC_METHOD_SMOKE_MUTE = "Smoke.Mute";
public static final String SHELLYRPC_METHOD_SCRIPT_LIST = "Script.List";
@ -151,6 +156,12 @@ public class Shelly2ApiJsonDTO {
public static final String SHELLY2_WAKEUPOCAUSE_UPDATE = "status_update";
public static final String SHELLY2_WAKEUPOCAUSE_UNDEFINED = "undefined";
// Dimmer US: LED power modes
public static final String SHELLY2_POWERLED_ON = "on";
public static final String SHELLY2_POWERLED_OFF = "off";
public static final String SHELLY2_POWERLED_MATCH = "match_output";
public static final String SHELLY2_POWERLED_INVERT = "inverted_output";
public class Shelly2DevConfigBle {
public Boolean enable;
}
@ -265,7 +276,7 @@ public class Shelly2ApiJsonDTO {
}
public class Shelly2DevConfigInput {
public String id;
public Integer id;
public String name;
public String type;
public Boolean invert;
@ -276,7 +287,7 @@ public class Shelly2ApiJsonDTO {
}
public class Shelly2DevConfigSwitch {
public String id;
public Integer id;
public String name;
@SerializedName("in_mode")
@ -367,6 +378,41 @@ public class Shelly2ApiJsonDTO {
public Boolean mute;
}
public static class Shelly2GetConfigLight {
public static class Shelly2GetConfigLightDefault {
public Integer brightness;
}
public static class Shelly2GetConfigLightNightMode {
public boolean enable;
public Integer brightness;
}
public Integer id;
public String name;
@SerializedName("initial_state")
public String initialState;
@SerializedName("auto_on")
public Boolean autoOn;
@SerializedName("auto_off")
public Boolean autoOff;
@SerializedName("auto_on_delay")
public Double autoOnDelay;
@SerializedName("auto_off_delay")
public Double autoOffDelay;
@SerializedName("default")
public Shelly2GetConfigLightDefault defaultCfg;
@SerializedName("night_mode")
public Shelly2GetConfigLightNightMode nightMode;
}
public class Shelly2DeviceConfigLed {
@SerializedName("sys_led_enable")
public Boolean sysLedEnable;
@SerializedName("power_led")
public String powerLed;
}
public static class Shelly2GetConfigResult {
public class Shelly2DevConfigCloud {
@ -392,6 +438,8 @@ public class Shelly2ApiJsonDTO {
public Shelly2DevConfigMqtt mqtt;
public Shelly2DeviceConfigSys sys;
public Shelly2DeviceConfigWiFi wifi;
@SerializedName("wd_ui")
public Shelly2DeviceConfigLed led;
@SerializedName("input:0")
public Shelly2DevConfigInput input0;
@ -417,6 +465,9 @@ public class Shelly2ApiJsonDTO {
@SerializedName("cover:0")
public Shelly2DevConfigCover cover0;
@SerializedName("light:0")
public Shelly2GetConfigLight light0;
@SerializedName("smoke:0")
public Shelly2ConfigSmoke smoke0;
}
@ -460,6 +511,17 @@ public class Shelly2ApiJsonDTO {
public ArrayList<String> errors;// shown only if at least one error is present.
}
public static class Shelly2DeviceStatusLight {
public Integer id;
public String source;
public Boolean output;
public Double brightness;
@SerializedName("timer_started_at")
public Double timerStartedAt;
@SerializedName("timer_duration")
public Integer timerDuration;
}
public static class Shelly2DeviceStatusResult {
public class Shelly2DeviceStatusBle {
@ -501,6 +563,12 @@ public class Shelly2ApiJsonDTO {
public Double rh;
}
public class Shelly2DeviceStatusIlluminance {
public Integer id;
public Double lux;
public String illumination;
}
public class Shelly2DeviceStatusVoltage {
public Integer id;
public Double voltage;
@ -618,6 +686,9 @@ public class Shelly2ApiJsonDTO {
@SerializedName("cover:0")
public Shelly2CoverStatus cover0;
@SerializedName("light:0")
public Shelly2DeviceStatusLight light0;
@SerializedName("temperature:0")
public Shelly2DeviceStatusTempId temperature0;
@SerializedName("temperature:100")
@ -635,6 +706,10 @@ public class Shelly2ApiJsonDTO {
public Shelly2DeviceStatusHumidity humidity0;
@SerializedName("humidity:100")
public Shelly2DeviceStatusHumidity humidity100;
@SerializedName("illuminance:0")
Shelly2DeviceStatusIlluminance illuminance0;
@SerializedName("smoke:0")
public Shelly2DeviceStatusSmoke smoke0;
@ -751,6 +826,12 @@ public class Shelly2ApiJsonDTO {
public Boolean autoOff;
@SerializedName("auto_off_delay")
public Double autoOffDelay;
// WD_UI.SetConfig
@SerializedName("sys_led_enable")
public Boolean sysLedEnable;
@SerializedName("power_led")
public String powerLed;
}
public static class Shelly2RpcRequest {
@ -764,6 +845,11 @@ public class Shelly2ApiJsonDTO {
public Integer pos;
public Boolean on;
// Dimmer / Light
public Integer brightness;
@SerializedName("toggle_after")
public Integer toggleAfter;
// Shelly.SetAuth
public String user;
public String realm;

View File

@ -41,6 +41,7 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheck
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySensorSleepMode;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsEMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsLogin;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsMeter;
@ -60,6 +61,7 @@ import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceC
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfigAp;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfigAp.Shelly2DeviceConfigApRE;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceSettings;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusLight;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusSys.Shelly2DeviceStatusSysAvlUpdate;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2NotifyEvent;
@ -151,11 +153,9 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
@Override
public void startScan() {
if (config.enableBluGateway) {
try {
installScript(SHELLY2_BLU_GWSCRIPT);
} catch (ShellyApiException e) {
}
try {
installScript(SHELLY2_BLU_GWSCRIPT, config.enableBluGateway);
} catch (ShellyApiException e) {
}
}
@ -206,6 +206,7 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
profile.deviceType = device.type;
profile.mac = device.mac;
profile.auth = device.auth;
profile.isGen2 = device.gen == 2;
if (config.serviceName.isEmpty()) {
config.serviceName = getString(profile.hostname);
}
@ -275,7 +276,13 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
}
}
profile.status.dimmers = profile.isDimmer ? new ArrayList<>() : null;
if (profile.isDimmer) {
profile.settings.dimmers = new ArrayList<>();
profile.settings.dimmers.add(new ShellySettingsDimmer());
profile.status.dimmers = new ArrayList<>();
profile.status.dimmers.add(new ShellyShortLightStatus());
fillDimmerSettings(profile, dc);
}
profile.status.lights = profile.isBulb ? new ArrayList<>() : null;
profile.status.thermostats = profile.isTRV ? new ArrayList<>() : null;
@ -286,19 +293,25 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
checkSetWsCallback();
}
if (dc.led != null) {
profile.settings.ledStatusDisable = !getBool(dc.led.sysLedEnable);
profile.settings.ledPowerDisable = getString(dc.led.powerLed).equals("off");
}
profile.initialized = true;
if (!discovery) {
getStatus(); // make sure profile.status is initialized (e.g,. relay/meter status)
asyncApiRequest(SHELLYRPC_METHOD_GETSTATUS); // request periodic status updates from device
try {
logger.debug("{}: BLU Gateway support enabled for this device: {}", thingName, config.enableBluGateway);
if (config.enableBluGateway) {
if (getBool(profile.settings.bluetooth)) {
installScript(SHELLY2_BLU_GWSCRIPT);
} else {
logger.debug("{}: Bluetooth needs to be enabled to activate BLU Gateway mode", thingName);
if (config.enableBluGateway != null) {
logger.debug("{}: BLU Gateway support is {} for this device", thingName,
config.enableBluGateway ? "enabled" : "disabled");
boolean bluetooth = getBool(profile.settings.bluetooth);
if (config.enableBluGateway && !bluetooth) {
logger.info("{}: Bluetooth needs to be enabled to activate BLU Gateway mode", thingName);
}
installScript(SHELLY2_BLU_GWSCRIPT, config.enableBluGateway && bluetooth);
}
} catch (ShellyApiException e) {
logger.debug("{}: Device config failed", thingName, e);
@ -340,24 +353,34 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
}
}
protected void installScript(String script) throws ShellyApiException {
String json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_LIST));
ShellyScriptListResponse scriptList = gson.fromJson(json, ShellyScriptListResponse.class);
protected void installScript(String script, boolean install) throws ShellyApiException {
ShellyScriptListResponse scriptList = apiRequest(
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_LIST), ShellyScriptListResponse.class);
Integer ourId = -1;
String code = "";
logger.debug("{}: Install or restart script {} on Shelly Device", thingName, script);
if (install) {
logger.debug("{}: Install or restart script {} on Shelly Device", thingName, script);
}
boolean running = false, upload = false;
if (scriptList != null) {
for (ShellyScriptListEntry s : scriptList.scripts) {
if (s.name.startsWith(script)) {
ourId = s.id;
running = s.running;
logger.debug("{}: Script {} is already installed, id={}", thingName, script, ourId);
}
for (ShellyScriptListEntry s : scriptList.scripts) {
if (s.name.startsWith(script)) {
ourId = s.id;
running = s.running;
logger.debug("{}: Script {} is already installed, id={}", thingName, script, ourId);
break;
}
}
if (!install) {
if (ourId != -1) {
startScript(ourId, false);
enableScript(script, false);
logger.debug("{}: Script {} was disabledd, id={}", thingName, script, ourId);
}
return;
}
// get script code from bundle resources
String file = BUNDLE_RESOURCE_SCRIPTS + "/" + script;
ClassLoader cl = Shelly2ApiRpc.class.getClassLoader();
@ -380,8 +403,9 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
} else {
try {
// verify that the same code version is active (avoid unnesesary flash updates)
json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_GETCODE).withId(ourId));
ShellyScriptResponse rsp = gson.fromJson(json, ShellyScriptResponse.class);
ShellyScriptResponse rsp = apiRequest(
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_GETCODE).withId(ourId),
ShellyScriptResponse.class);
if (!rsp.data.trim().equals(code.trim())) {
logger.debug("{}: A script version was found, update to newest one", thingName);
upload = true;
@ -397,27 +421,26 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
}
if (restart || (running && upload)) {
json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_STOP).withId(ourId));
// first stop running script
startScript(ourId, false);
running = false;
}
if (upload && ourId != -1) {
// Delete existing script
logger.debug("{}: Delete existing script", thingName);
json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_DELETE).withId(ourId));
apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_DELETE).withId(ourId));
}
if (upload) {
logger.debug("{}: Script will be installed...", thingName);
// Create new script, get id
json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_CREATE).withName(script));
ShellyScriptResponse rsp = gson.fromJson(json, ShellyScriptResponse.class);
if (rsp != null) {
ourId = rsp.id;
logger.debug("{}: Script has been created, id={}", thingName, ourId);
upload = true;
}
ShellyScriptResponse rsp = apiRequest(
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_CREATE).withName(script),
ShellyScriptResponse.class);
ourId = rsp.id;
logger.debug("{}: Script has been created, id={}", thingName, ourId);
upload = true;
}
if (upload) {
@ -437,17 +460,40 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
parms.append = true;
} while (processed < length);
running = false;
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams().withConfig();
params.config.enable = true;
apiRequest(SHELLYRPC_METHOD_SCRIPT_SETCONFIG, params, String.class);
}
if (enableScript(script, true)) {
logger.info("{}: Script {} was {} installed successful", thingName, thingName, script);
}
if (!running) {
// Script was created or is there and stopped -> start it
json = apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SCRIPT_START).withId(ourId));
logger.debug("{}: Script {} was {} successful", thingName, script,
restart ? "restarted" : "installed and started");
running = startScript(ourId, true);
}
logger.info("{}: Script {} {}", thingName, script,
running ? "was successfully (re)started" : "failed to start");
}
private boolean startScript(int ourId, boolean start) {
if (ourId != -1) {
try {
apiRequest(new Shelly2RpcRequest()
.withMethod(start ? SHELLYRPC_METHOD_SCRIPT_START : SHELLYRPC_METHOD_SCRIPT_STOP)
.withId(ourId));
return true;
} catch (ShellyApiException e) {
}
}
return false;
}
private boolean enableScript(String script, boolean enable) {
try {
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams().withConfig();
params.config.name = script;
params.config.enable = enable;
apiRequest(SHELLYRPC_METHOD_SCRIPT_SETCONFIG, params, String.class);
return true;
} catch (ShellyApiException e) {
return false;
}
}
@ -519,6 +565,15 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
toQuantityType(getDouble(status.tmp.tC), DIGITS_NONE, SIUnits.CELSIUS));
}
if (status.meters.size() > 0) {
boolean validMeter = false;
for (ShellySettingsMeter meter : status.meters) {
validMeter |= meter.isValid;
}
if (!validMeter) {
profile.numMeters = 0;
}
}
profile.status = status;
if (updated) {
getThing().restartWatchdog();
@ -765,6 +820,44 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_COVER_SETPOS).withId(relayIndex).withPos(position));
}
@Override
public ShellyStatusLight getLightStatus() throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public ShellyShortLightStatus getLightStatus(int index) throws ShellyApiException {
ShellyShortLightStatus status = new ShellyShortLightStatus();
Shelly2DeviceStatusLight ls = apiRequest(
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_LIGHT_STATUS).withId(index),
Shelly2DeviceStatusLight.class);
status.ison = ls.output;
status.hasTimer = ls.timerStartedAt != null;
status.timerDuration = getDuration(ls.timerStartedAt, ls.timerDuration);
if (ls.brightness != null) {
status.brightness = ls.brightness.intValue();
}
return status;
}
@Override
public void setBrightness(int id, int brightness, boolean autoOn) throws ShellyApiException {
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams();
params.id = id;
params.brightness = brightness;
params.on = brightness > 0;
apiRequest(SHELLYRPC_METHOD_LIGHT_SET, params, String.class);
}
@Override
public ShellyShortLightStatus setLightTurn(int id, String turnMode) throws ShellyApiException {
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams();
params.id = id;
params.on = turnMode.equals(SHELLY_API_ON);
apiRequest(SHELLYRPC_METHOD_LIGHT_SET, params, String.class);
return getLightStatus(id);
}
@Override
public ShellyStatusSensor getSensorStatus() throws ShellyApiException {
return sensorData;
@ -772,10 +865,13 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
@Override
public void setAutoTimer(int index, String timerName, double value) throws ShellyApiException {
Shelly2RpcRequest req = new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SWITCH_SETCONFIG).withId(index);
ShellyDeviceProfile profile = getProfile();
boolean isLight = profile.isLight || profile.isDimmer;
String method = isLight ? SHELLYRPC_METHOD_LIGHT_SETCONFIG : SHELLYRPC_METHOD_SWITCH_SETCONFIG;
String component = isLight ? "Light" : "Switch";
Shelly2RpcRequest req = new Shelly2RpcRequest().withMethod(method).withId(index);
req.params.withConfig();
req.params.config.name = "Switch" + index;
req.params.config.name = component + index;
if (timerName.equals(SHELLY_TIMER_AUTOON)) {
req.params.config.autoOn = value > 0;
req.params.config.autoOnDelay = value;
@ -786,8 +882,23 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
apiRequest(req);
}
@Override
public void setLedStatus(String ledName, boolean value) throws ShellyApiException {
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams().withConfig();
params.id = 0;
if (ledName.equals(SHELLY_LED_STATUS_DISABLE)) {
params.config.sysLedEnable = value;
} else if (ledName.equals(SHELLY_LED_POWER_DISABLE)) {
params.config.powerLed = value ? SHELLY2_POWERLED_OFF : SHELLY2_POWERLED_MATCH;
} else {
throw new ShellyApiException("API call not implemented for this LED type");
}
apiRequest(SHELLYRPC_METHOD_LED_SETCONFIG, params, Shelly2WsConfigResult.class);
}
@Override
public void resetMeterTotal(int id) throws ShellyApiException {
apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_EMDATARESET).withId(id));
}
@Override
@ -899,20 +1010,6 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
* The following API calls are not yet relevant, because currently there a no Plus/Pro (Gen2) devices of those
* categories (e.g. bulbs)
*/
@Override
public void setLedStatus(String ledName, boolean value) throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public ShellyStatusLight getLightStatus() throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public ShellyShortLightStatus getLightStatus(int index) throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public void setLightParm(int lightIndex, String parm, String value) throws ShellyApiException {
@ -924,16 +1021,6 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
throw new ShellyApiException("API call not implemented");
}
@Override
public ShellyShortLightStatus setLightTurn(int id, String turnMode) throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public void setBrightness(int id, int brightness, boolean autoOn) throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
}
@Override
public void setLightMode(String mode) throws ShellyApiException {
throw new ShellyApiException("API call not implemented");
@ -1058,10 +1145,12 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
if (response.result != null) {
// return sub element result as requested class type
json = gson.toJson(gson.fromJson(json, Shelly2RpcBaseMessage.class).result);
return fromJson(gson, json, classOfT);
boolean isString = response.result instanceof String;
return fromJson(gson, isString && ((String) response.result).equalsIgnoreCase("null") ? "{}" : json,
classOfT);
} else {
// return direct format
return gson.fromJson(json, classOfT);
return gson.fromJson(json, classOfT == String.class ? Shelly2RpcBaseMessage.class : classOfT);
}
}
@ -1069,8 +1158,8 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
return apiRequest(request.method, request.params, classOfT);
}
public String apiRequest(Shelly2RpcRequest request) throws ShellyApiException {
return apiRequest(request.method, request.params, String.class);
public void apiRequest(Shelly2RpcRequest request) throws ShellyApiException {
apiRequest(request.method, request.params, Shelly2RpcBaseMessage.class);
}
private String rpcPost(String postData) throws ShellyApiException {

View File

@ -24,15 +24,15 @@ import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2RpcNoti
@NonNullByDefault
public interface Shelly2RpctInterface {
public void onConnect(String deviceIp, boolean connected);
void onConnect(String deviceIp, boolean connected);
public void onMessage(String decodedmessage);
void onMessage(String decodedmessage);
public void onNotifyStatus(Shelly2RpcNotifyStatus message);
void onNotifyStatus(Shelly2RpcNotifyStatus message);
public void onNotifyEvent(Shelly2RpcNotifyEvent message);
void onNotifyEvent(Shelly2RpcNotifyEvent message);
public void onClose(int statusCode, String reason);
void onClose(int statusCode, String reason);
public void onError(Throwable cause);
void onError(Throwable cause);
}

View File

@ -44,5 +44,5 @@ public class ShellyThingConfiguration {
public String localPort = "8080";
public String serviceName = "";
public boolean enableBluGateway = false;
public Boolean enableBluGateway = false;
}

View File

@ -79,6 +79,8 @@ public class ShellyThingCreator {
public static final String SHELLYDT_PLUSI4DC = "SNSN-0D24X";
public static final String SHELLYDT_PLUSHT = "SNSN-0013A";
public static final String SHELLYDT_PLUSSMOKE = "SNSN-0031Z";
public static final String SHELLYDT_PLUSDIMMERUS = "SNDM-0013US";
public static final String SHELLYDT_PLUSWALLDISPLAY = "SAWD-0A1XX10EU1";
// Shelly Pro Series
public static final String SHELLYDT_PRO1 = "SPSW-001XE16EU";
@ -157,6 +159,8 @@ public class ShellyThingCreator {
public static final String THING_TYPE_SHELLYPLUSSMOKE_STR = "shellyplussmoke";
public static final String THING_TYPE_SHELLYPLUSPLUGS_STR = "shellyplusplug";
public static final String THING_TYPE_SHELLYPLUSPLUGUS_STR = "shellyplusplugus";
public static final String THING_TYPE_SHELLYPLUSDIMMERUS_STR = "shellypluswdus";
public static final String THING_TYPE_SHELLYPLUSWALLDISPLAY_STR = "shellywalldisplay";
// Shelly Pro Series
public static final String THING_TYPE_SHELLYPRO1_STR = "shellypro1";
@ -233,7 +237,7 @@ public class ShellyThingCreator {
public static final ThingTypeUID THING_TYPE_SHELLYUNKNOWN = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYUNKNOWN_STR);
// Shelly Plus/Pro
// Shelly Plus
public static final ThingTypeUID THING_TYPE_SHELLYPLUS1 = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYPLUS1_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUS1PM = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUS1PM_STR);
@ -253,6 +257,12 @@ public class ShellyThingCreator {
THING_TYPE_SHELLYPLUSPLUGS_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSPLUGUS = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUSPLUGUS_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSDIMMERUS = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUSDIMMERUS_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSWALLDISPLAY = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUSWALLDISPLAY_STR);
// Shelly Pro
public static final ThingTypeUID THING_TYPE_SHELLYPRO1 = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYPRO1_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPRO1PM = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPRO1PM_STR);
@ -317,6 +327,7 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(SHELLYDT_PLUSI4, THING_TYPE_SHELLYPLUSI4_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSHT, THING_TYPE_SHELLYPLUSHT_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSSMOKE, THING_TYPE_SHELLYPLUSSMOKE_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSDIMMERUS, THING_TYPE_SHELLYPLUSDIMMERUS_STR);
// Pro Series
THING_TYPE_MAPPING.put(SHELLYDT_PRO1, THING_TYPE_SHELLYPRO1_STR);
@ -343,6 +354,9 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(SHELLYDT_BLUBUTTON, THING_TYPE_SHELLYBLUBUTTON_STR);
THING_TYPE_MAPPING.put(SHELLYDT_BLUDW, THING_TYPE_SHELLYBLUDW_STR);
// Wall displays
THING_TYPE_MAPPING.put(SHELLYDT_PLUSWALLDISPLAY, THING_TYPE_SHELLYPLUSWALLDISPLAY_STR);
// mapping by thing type
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1_STR, THING_TYPE_SHELLY1_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1PM_STR, THING_TYPE_SHELLY1PM_STR);
@ -385,6 +399,8 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSI4_STR, THING_TYPE_SHELLYPLUSI4_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSHT_STR, THING_TYPE_SHELLYPLUSHT_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSSMOKE_STR, THING_TYPE_SHELLYPLUSSMOKE_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSDIMMERUS_STR, THING_TYPE_SHELLYPLUSDIMMERUS_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSWALLDISPLAY_STR, THING_TYPE_SHELLYPLUSWALLDISPLAY_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO1_STR, THING_TYPE_SHELLYPRO1_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO1PM_STR, THING_TYPE_SHELLYPRO1PM_STR);

View File

@ -20,22 +20,28 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsEMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyADC;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyExtTemperature.ShellyShortTemp;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyThermnostat;
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.UnDefType;
import com.google.gson.Gson;
/***
* The{@link ShellyComponents} implements updates for supplemental components
* Meter will be used by Relay + Light; Sensor is part of H&T, Flood, Door Window, Sense
@ -69,14 +75,12 @@ public class ShellyComponents {
Integer rssi = getInteger(status.wifiSta.rssi);
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_RSSI, mapSignalStrength(rssi));
if (getDouble(status.temperature) != SHELLY_API_INVTEMP) {
if (status.tmp != null && !thingHandler.getProfile().isSensor) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.tmp.tC), DIGITS_NONE, SIUnits.CELSIUS));
} else if (status.temperature != null) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.temperature), DIGITS_NONE, SIUnits.CELSIUS));
}
if (status.tmp != null && !thingHandler.getProfile().isSensor && status.tmp.tC != SHELLY_API_INVTEMP) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.tmp.tC), DIGITS_NONE, SIUnits.CELSIUS));
} else if (status.temperature != null && status.temperature != SHELLY_API_INVTEMP) {
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
toQuantityType(getDouble(status.temperature), DIGITS_NONE, SIUnits.CELSIUS));
}
thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_SLEEPTIME,
toQuantityType(getInteger(status.sleepTime), Units.SECOND));
@ -268,8 +272,8 @@ public class ShellyComponents {
.createEMeterChannels(thingHandler.getThing(), profile, emeter, groupName));
}
// convert Watt/Hour to kw/h
double total = getDouble(emeter.total) / 1000 / 60;
// convert Watt/h to KW/h
double total = getDouble(emeter.total) / 1000;
double totalReturned = getDouble(emeter.totalReturned) / 1000;
updated |= thingHandler.updateChannel(groupName, CHANNEL_METER_CURRENTWATTS,
toQuantityType(getDouble(emeter.power), DIGITS_WATT, Units.WATT));
@ -332,7 +336,7 @@ public class ShellyComponents {
// convert totalWatts into kw/h
totalWatts = totalWatts / (60.0 * 1000.0);
updated |= thingHandler.updateChannel(groupName, CHANNEL_METER_CURRENTWATTS,
toQuantityType(getDouble(currentWatts), DIGITS_WATT, Units.WATT));
toQuantityType(currentWatts, DIGITS_WATT, Units.WATT));
updated |= thingHandler.updateChannel(groupName, CHANNEL_METER_TOTALKWH,
toQuantityType(totalWatts, DIGITS_KWH, Units.KILOWATT_HOUR));
@ -537,6 +541,64 @@ public class ShellyComponents {
return updated;
}
public static boolean updateDimmers(ShellyThingInterface thingHandler, ShellySettingsStatus orgStatus)
throws ShellyApiException {
boolean updated = false;
ShellyDeviceProfile profile = thingHandler.getProfile();
if (profile.isDimmer) {
// We need to fixup the returned Json: The dimmer returns light[] element, which is ok, but it doesn't have
// the same structure as lights[] from Bulb,RGBW2 and Duo. The tag gets replaced by dimmers[] so that Gson
// maps to a different structure (ShellyShortLight).
Gson gson = new Gson();
ShellySettingsStatus dstatus = !profile.isGen2
? fromJson(gson, Shelly1ApiJsonDTO.fixDimmerJson(orgStatus.json), ShellySettingsStatus.class)
: orgStatus;
int l = 0;
for (ShellyShortLightStatus dimmer : dstatus.dimmers) {
Integer r = l + 1;
String groupName = profile.numRelays <= 1 ? CHANNEL_GROUP_DIMMER_CONTROL
: CHANNEL_GROUP_DIMMER_CONTROL + r.toString();
if (!thingHandler.areChannelsCreated()) {
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions
.createDimmerChannels(thingHandler.getThing(), profile, dstatus, l));
}
ShellySettingsDimmer ds = profile.settings.dimmers.get(l);
if (ds.name != null) {
updated |= thingHandler.updateChannel(groupName, CHANNEL_OUTPUT_NAME, getStringType(ds.name));
}
// On a status update we map a dimmer.ison = false to brightness 0 rather than the device's brightness
// and send an OFF status to the same channel.
// When the device's brightness is > 0 we send the new value to the channel and an ON command
if (dimmer.ison) {
updated |= thingHandler.updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.ON);
updated |= thingHandler.updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
toQuantityType((double) getInteger(dimmer.brightness), DIGITS_NONE, Units.PERCENT));
} else {
updated |= thingHandler.updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.OFF);
updated |= thingHandler.updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
toQuantityType(0.0, DIGITS_NONE, Units.PERCENT));
}
if (profile.settings.dimmers != null) {
ShellySettingsDimmer dsettings = profile.settings.dimmers.get(l);
if (dsettings != null) {
updated |= thingHandler.updateChannel(groupName, CHANNEL_TIMER_AUTOON,
toQuantityType(getDouble(dsettings.autoOn), Units.SECOND));
updated |= thingHandler.updateChannel(groupName, CHANNEL_TIMER_AUTOOFF,
toQuantityType(getDouble(dsettings.autoOff), Units.SECOND));
}
}
l++;
}
}
return updated;
}
public static boolean updateTempChannel(@Nullable ShellyShortTemp sensor, ShellyThingInterface thingHandler,
String channel) {
return sensor != null ? updateTempChannel(thingHandler, CHANNEL_GROUP_SENSOR, channel, sensor.tC, "") : false;

View File

@ -28,6 +28,6 @@ public interface ShellyDeviceListener {
/**
* This method is called when new device information is received.
*/
public boolean onEvent(String ipAddress, String deviceName, String deviceIndex, String eventType,
boolean onEvent(String ipAddress, String deviceName, String deviceIndex, String eventType,
Map<String, String> parameters);
}

View File

@ -19,9 +19,7 @@ import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
@ -42,8 +40,6 @@ import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/***
* The{@link ShellyRelayHandler} handles light (bulb+rgbw2) specific commands and status. All other commands will be
* handled by the generic thing handler.
@ -193,7 +189,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
if (brightness > 0) {
api.setBrightness(lightId, brightness, config.brightnessAutoOn);
} else {
api.setRelayTurn(lightId, power == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
api.setLightTurn(lightId, power == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
if (brightness >= 0) { // ignore -1
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value",
toQuantityType((double) (power == OnOffType.ON ? brightness : 0), DIGITS_NONE, Units.PERCENT));
@ -206,7 +202,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
// map status to channels
boolean updated = false;
updated |= updateRelays(status);
updated |= updateDimmers(status);
updated |= ShellyComponents.updateDimmers(this, status);
updated |= updateLed(status);
return updated;
}
@ -300,12 +296,6 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
}
}
private void createDimmerChannels(ShellySettingsStatus dstatus, int idx) {
if (!areChannelsCreated()) {
updateChannelDefinitions(ShellyChannelDefinitions.createDimmerChannels(getThing(), profile, dstatus, idx));
}
}
private void createRollerChannels(ShellyRollerStatus roller) {
if (!areChannelsCreated()) {
updateChannelDefinitions(ShellyChannelDefinitions.createRollerChannels(getThing(), roller));
@ -356,63 +346,6 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
return updated;
}
/**
* Update Relay/Roller channels
*
* @param th Thing Handler instance
* @param profile ShellyDeviceProfile
* @param status Last ShellySettingsStatus
*
* @throws ShellyApiException
*/
public boolean updateDimmers(ShellySettingsStatus orgStatus) throws ShellyApiException {
boolean updated = false;
if (profile.isDimmer) {
// We need to fixup the returned Json: The dimmer returns light[] element, which is ok, but it doesn't have
// the same structure as lights[] from Bulb,RGBW2 and Duo. The tag gets replaced by dimmers[] so that Gson
// maps to a different structure (ShellyShortLight).
Gson gson = new Gson();
ShellySettingsStatus dstatus = fromJson(gson, Shelly1ApiJsonDTO.fixDimmerJson(orgStatus.json),
ShellySettingsStatus.class);
logger.trace("{}: Updating {} dimmers(s)", thingName, dstatus.dimmers.size());
int l = 0;
for (ShellyShortLightStatus dimmer : dstatus.dimmers) {
Integer r = l + 1;
String groupName = profile.numRelays <= 1 ? CHANNEL_GROUP_DIMMER_CONTROL
: CHANNEL_GROUP_DIMMER_CONTROL + r.toString();
createDimmerChannels(dstatus, l);
// On a status update we map a dimmer.ison = false to brightness 0 rather than the device's brightness
// and send an OFF status to the same channel.
// When the device's brightness is > 0 we send the new value to the channel and an ON command
if (dimmer.ison) {
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.ON);
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
toQuantityType((double) getInteger(dimmer.brightness), DIGITS_NONE, Units.PERCENT));
} else {
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.OFF);
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
toQuantityType(0.0, DIGITS_NONE, Units.PERCENT));
}
if (profile.settings.dimmers != null) {
ShellySettingsDimmer dsettings = profile.settings.dimmers.get(l);
if (dsettings != null) {
updated |= updateChannel(groupName, CHANNEL_TIMER_AUTOON,
toQuantityType(getDouble(dsettings.autoOn), Units.SECOND));
updated |= updateChannel(groupName, CHANNEL_TIMER_AUTOOFF,
toQuantityType(getDouble(dsettings.autoOff), Units.SECOND));
}
}
l++;
}
}
return updated;
}
/**
* Update LED channels
*

View File

@ -39,85 +39,86 @@ import org.openhab.core.types.StateOption;
@NonNullByDefault
public interface ShellyThingInterface {
public ShellyDeviceProfile getProfile(boolean forceRefresh) throws ShellyApiException;
ShellyDeviceProfile getProfile(boolean forceRefresh) throws ShellyApiException;
public @Nullable List<StateOption> getStateOptions(ChannelTypeUID uid);
@Nullable
List<StateOption> getStateOptions(ChannelTypeUID uid);
public double getChannelDouble(String group, String channel);
double getChannelDouble(String group, String channel);
public boolean updateChannel(String group, String channel, State value);
boolean updateChannel(String group, String channel, State value);
public boolean updateChannel(String channelId, State value, boolean force);
boolean updateChannel(String channelId, State value, boolean force);
public void setThingOnline();
void setThingOnline();
public void setThingOffline(ThingStatusDetail detail, String messageKey, Object... arguments);
void setThingOffline(ThingStatusDetail detail, String messageKey, Object... arguments);
public boolean isStopping();
boolean isStopping();
public String getThingType();
String getThingType();
public ThingStatus getThingStatus();
ThingStatus getThingStatus();
public ThingStatusDetail getThingStatusDetail();
ThingStatusDetail getThingStatusDetail();
public boolean isThingOnline();
boolean isThingOnline();
public boolean requestUpdates(int requestCount, boolean refreshSettings);
boolean requestUpdates(int requestCount, boolean refreshSettings);
public void triggerUpdateFromCoap();
void triggerUpdateFromCoap();
public void reinitializeThing();
void reinitializeThing();
public void restartWatchdog();
void restartWatchdog();
public void publishState(String channelId, State value);
void publishState(String channelId, State value);
public boolean areChannelsCreated();
boolean areChannelsCreated();
public State getChannelValue(String group, String channel);
State getChannelValue(String group, String channel);
public boolean updateInputs(ShellySettingsStatus status);
boolean updateInputs(ShellySettingsStatus status);
public void updateChannelDefinitions(Map<String, Channel> dynChannels);
void updateChannelDefinitions(Map<String, Channel> dynChannels);
public void postEvent(String event, boolean force);
void postEvent(String event, boolean force);
public void triggerChannel(String group, String channelID, String event);
void triggerChannel(String group, String channelID, String event);
public void triggerButton(String group, int idx, String value);
void triggerButton(String group, int idx, String value);
public ShellyDeviceStats getStats();
ShellyDeviceStats getStats();
public void resetStats();
void resetStats();
public Thing getThing();
Thing getThing();
public String getThingName();
String getThingName();
public ShellyThingConfiguration getThingConfig();
ShellyThingConfiguration getThingConfig();
public HttpClient getHttpClient();
HttpClient getHttpClient();
public String getProperty(String key);
String getProperty(String key);
public void updateProperties(String key, String value);
void updateProperties(String key, String value);
public boolean updateWakeupReason(@Nullable List<Object> valueArray);
boolean updateWakeupReason(@Nullable List<Object> valueArray);
public ShellyApiInterface getApi();
ShellyApiInterface getApi();
public ShellyDeviceProfile getProfile();
ShellyDeviceProfile getProfile();
public long getScheduledUpdates();
long getScheduledUpdates();
public void fillDeviceStatus(ShellySettingsStatus status, boolean updated);
void fillDeviceStatus(ShellySettingsStatus status, boolean updated);
public boolean checkRepresentation(String key);
boolean checkRepresentation(String key);
public void incProtMessages();
void incProtMessages();
public void incProtErrors();
void incProtErrors();
public void startScan();
void startScan();
}

View File

@ -78,7 +78,7 @@ public class ShellyChannelDefinitions {
public static final String ITEMT_POWER = "Number:Power";
public static final String ITEMT_ENERGY = "Number:Energy";
public static final String ITEMT_VOLT = "Number:ElectricPotential";
public static final String ITEMT_AMP = "Number:ElectricPotential";
public static final String ITEMT_AMP = "Number:ElectricCurrent";
public static final String ITEMT_ANGLE = "Number:Angle";
public static final String ITEMT_DISTANCE = "Number:Length";
public static final String ITEMT_SPEED = "Number:Speed";
@ -129,8 +129,8 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ITEMP, "deviceTemp", ITEMT_TEMP))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_WAKEUP, "sensorWakeup", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS, "meterAccuWatts", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEMT_ENERGY))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEMT_ENERGY))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_VOLTAGE, "supplyVoltage", ITEMT_VOLT))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CHARGER, "charger", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE, "ledStatusDisable", ITEMT_SWITCH))
@ -191,7 +191,7 @@ public class ShellyChannelDefinitions {
// Power Meter
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEMT_ENERGY))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEMT_ENERGY))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
// EMeter
@ -215,7 +215,7 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_TS, "motionTimestamp", ITEMT_DATETIME))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_ACT, "motionActive", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "vibration", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_FLOOD, "sensorFlood", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SMOKE, "sensorSmoke", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MUTE, "sensorMute", ITEMT_SWITCH))
@ -304,13 +304,14 @@ public class ShellyChannelDefinitions {
&& ((status.temperature != null && getDouble(status.temperature) != SHELLY_API_INVTEMP)
|| (status.tmp != null && getDouble(status.tmp.tC) != SHELLY_API_INVTEMP))) {
// Only some devices report the internal device temp
addChannel(thing, add, status.tmp != null || status.temperature != null, CHGR_DEVST, CHANNEL_DEVST_ITEMP);
addChannel(thing, add,
!profile.isLight && (status.temperature != null || (status.tmp != null && !profile.isSensor)),
CHGR_DEVST, CHANNEL_DEVST_ITEMP);
}
addChannel(thing, add, profile.settings.sleepTime != null, CHGR_SENSOR, CHANNEL_SENSOR_SLEEPTIME);
// If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
boolean accuChannel = (((status.meters != null) && (status.meters.size() > 1) && !profile.isRoller
&& !profile.isRGBW2) || ((status.emeters != null && status.emeters.size() > 1)));
boolean accuChannel = profile.numMeters > 1 && !profile.isRoller && !profile.isRGBW2;
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
@ -350,9 +351,11 @@ public class ShellyChannelDefinitions {
}
// Shelly 1/1PM and Plus 1/1PM Addon
if (profile.settings.extSwitch != null && profile.settings.extSwitch.input0 != null
&& idx == getInteger(profile.settings.extSwitch.input0.relayNum)) {
addChannel(thing, add, true, CHGR_SENSOR, CHANNEL_ESENSOR_INPUT + (idx + 1));
boolean addon = profile.settings.extSwitch != null && profile.settings.extSwitch.input0 != null
&& idx == getInteger(profile.settings.extSwitch.input0.relayNum);
if (addon) {
addChannel(thing, add, addon, CHGR_SENSOR,
CHANNEL_ESENSOR_INPUT + (profile.settings.extSwitch.input0.relayNum + 1));
}
if (profile.status.extTemperature != null) {
addChannel(thing, add, profile.status.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENSOR_TEMP1);
@ -361,7 +364,8 @@ public class ShellyChannelDefinitions {
addChannel(thing, add, profile.status.extTemperature.sensor4 != null, CHGR_SENSOR, CHANNEL_ESENSOR_TEMP4);
addChannel(thing, add, profile.status.extTemperature.sensor5 != null, CHGR_SENSOR, CHANNEL_ESENSOR_TEMP5);
}
addChannel(thing, add, profile.status.extHumidity != null, CHGR_SENSOR, CHANNEL_ESENSOR_HUMIDITY);
addChannel(thing, add, profile.status.extHumidity != null && profile.status.extHumidity.sensor1 != null,
CHGR_SENSOR, CHANNEL_ESENSOR_HUMIDITY);
addChannel(thing, add, profile.status.extVoltage != null, CHGR_SENSOR, CHANNEL_ESENSOR_VOLTAGE);
addChannel(thing, add, profile.status.extDigitalInput != null, CHGR_SENSOR, CHANNEL_ESENSOR_DIGITALINPUT);
addChannel(thing, add, profile.status.extAnalogInput != null, CHGR_SENSOR, CHANNEL_ESENSOR_ANALOGINPUT);
@ -379,6 +383,7 @@ public class ShellyChannelDefinitions {
if (profile.settings.dimmers != null) {
ShellySettingsDimmer ds = profile.settings.dimmers.get(idx);
addChannel(thing, add, ds.name != null, group, CHANNEL_OUTPUT_NAME);
addChannel(thing, add, ds.autoOn != null, group, CHANNEL_TIMER_AUTOON);
addChannel(thing, add, ds.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
ShellyShortLightStatus dss = dstatus.dimmers.get(idx);
@ -473,7 +478,7 @@ public class ShellyChannelDefinitions {
addChannel(thing, newChannels, emeter.voltage != null, group, CHANNEL_EMETER_VOLTAGE);
addChannel(thing, newChannels, emeter.current != null, group, CHANNEL_EMETER_CURRENT);
addChannel(thing, newChannels, emeter.pf != null, group, CHANNEL_EMETER_PFACTOR); // EM has no PF. but power
addChannel(thing, newChannels, emeter.total != null && profile.numMeters > 1, group, CHANNEL_EMETER_RESETTOTAL); // 3EM
addChannel(thing, newChannels, profile.is3EM, group, CHANNEL_EMETER_RESETTOTAL); // 3EM
addChannel(thing, newChannels, true, group, CHANNEL_LAST_UPDATE);
return newChannels;
}
@ -505,7 +510,8 @@ public class ShellyChannelDefinitions {
addChannel(thing, newChannels, sdata.sensor.vibration != null, CHANNEL_GROUP_SENSOR,
CHANNEL_SENSOR_VIBRATION);
}
if (sdata.accel != null) { // DW2
// Create tilt for DW/DW2, for BLU DW create channel even tilt is currently not reported
if (sdata.accel != null || (profile.isBlu && sdata.lux != null)) {
addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
}

View File

@ -89,4 +89,33 @@
<advanced>true</advanced>
</parameter>
</config-description>
<config-description uri="thing-type:shelly:dimmer-gen2">
<parameter name="deviceIp" type="text" required="true">
<label>@text/thing-type.config.shelly.deviceIp.label</label>
<description>@text/thing-type.config.shelly.deviceIp.description</description>
<context>network-address</context>
</parameter>
<parameter name="userId" type="text" required="false">
<label>@text/thing-type.config.shelly.userId.label</label>
<description>@text/thing-type.config.shelly.userId.description</description>
</parameter>
<parameter name="password" type="text" required="false">
<label>@text/thing-type.config.shelly.password.label</label>
<description>@text/thing-type.config.shelly.password.description</description>
<context>password</context>
</parameter>
<parameter name="brightnessAutoOn" type="boolean" required="false">
<label>@text/thing-type.config.shelly.light.brightnessAutoOn.label</label>
<description>@text/thing-type.config.shelly.light.brightnessAutoOn.description</description>
<default>true</default>
</parameter>
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
<label>@text/thing-type.config.shelly.updateInterval.label</label>
<description>@text/thing-type.config.shelly.updateInterval.description</description>
<default>60</default>
<unitLabel>seconds</unitLabel>
<advanced>true</advanced>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -88,6 +88,22 @@ thing-type.shelly.shellytrv.description = Shelly TRV (Radiator value, battery po
thing-type.shelly.shellyix3.description = Shelly ix3 (Activation Device with 3 inputs)
thing-type.shelly.shellypludht.description = Shelly Plus HT - Temperature and Humidity Sensor
# Plus Devices
thing-type.shelly.shellyplus1.description = Shelly Plus 1 (Single Relay Switch)
thing-type.shelly.shellyplus1pm.description = Shelly Plus 1PM - Single Relay Switch with Power Meter
thing-type.shelly.shellyplus2-relay.description = Shelly Plus 2PM - Dual Relay Switch with Power Meter
thing-type.shelly.shellyplus2pm-roller.description = Shelly Plus 2PM - Roller Control with Power Meter
thing-type.shelly.shellyplusplug.description = Shelly Plus Plug S/IT/UK/US . Outlet with Power Meter
thing-type.shelly.shellyplusi4.description = Shelly Plus i4 - 4xInput Device
thing-type.shelly.shellyplusi4dc.description = Shelly Plus i4DC - 4xDC Input Device
thing-type.shelly.shellyplusht.description = Shelly Plus HT - Humidity and Temperature sensor with display
thing-type.shelly.shellyplussmoke.description = Shelly Plus Smoke - Smoke Detector with Alarm
thing-type.shelly.shellyplussmoke.description = Shelly Plus Smoke - Smoke Detector with Alarm
thing-type.shelly.shellypluswdus.description = Shelly Wall Dimmer US Device
# Wall displays
thing-type.shelly.shellywalldisplay.description = Shelly Plus Wall Display with sensors and input/output
# Pro Devices
thing-type.shelly.shellypro1.description = Shelly Pro 1 - Single Relay Switch
thing-type.shelly.shellypro1pm.description = Shelly Pro 1PM - Single Relay Switch with Power Meter
@ -97,31 +113,10 @@ thing-type.shelly.shellypro2pm-roller.description = Shelly Pro 2PM - Roller Cont
thing-type.shelly.shellypro3.description = Shelly Pro 3 - 3xRelay Switch
thing-type.shelly.shellypro3em.description = Shelly Pro 3EM - 3xPower Meter
thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch with Power Meter
# Plus devices
thing-type.shelly.shellyplus1.description = Shelly Plus 1 (Single Relay Switch)
thing-type.shelly.shellyplus1pm.description = Shelly Plus 1PM - Single Relay Switch with Power Meter
thing-type.shelly.shellyplus2-relay.description = Shelly Plus 2PM - Dual Relay Switch with Power Meter
thing-type.shelly.shellyplus2pm-roller.description = Shelly Plus 2PM - Roller Control with Power Meter
thing-type.shelly.shellyplusplug.description = Shelly Plus Plug S/IT/UK/US . Outlet with Power Meter
thing-type.shelly.shellyplusht.description = Shelly Plus HT - Humidity and Temperature sensor with display
thing-type.shelly.shellyplussmoke.description = Shelly Plus Smoke - Smoke Detector with Alarm
thing-type.shelly.shellyplusi4.description = Shelly Plus i4 - 4xInput Device
thing-type.shelly.shellyplusi4dc.description = Shelly Plus i4DC - 4xDC Input Device
# Pro devices
thing-type.shelly.shellypro1.description = Shelly Pro 1 - Single Relay Switch
thing-type.shelly.shellypro1pm.description = Shelly Pro 1PM - Single Relay Switch with Power Meter
thing-type.shelly.shellypro2-relay.description = Shelly Pro 2 - Dual Relay Switch
thing-type.shelly.shellypro2-roller.description = Shelly Pro 2 - Roller Control
thing-type.shelly.shellypro2pm-relay.description= Shelly Pro 2PM - Dual Relay Switch with Power Meter
thing-type.shelly.shellypro2pm-roller.description = Shelly Pro 2PM - Roller Control with Power Meter
thing-type.shelly.shellypro3.description = Shelly Pro 3 - 3xRelay Switch
thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch with Power Meter
# BLU devices
thing-type.shelly.shellypblubutton.description = Shelly BLU Button
thing-type.shelly.shellybludw.description = Shelly BLU Door/Window Sensor
# BLU devices
thing-type.shelly.shellypblubutton.description = Shelly BLU Button
thing-type.shelly.shellybludw.description = Shelly BLU Door/Window Sensor
# thing config - shellydevice
thing-type.config.shelly.deviceIp.label = IP Address

View File

@ -479,7 +479,7 @@
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Current</tag>
<tag>Power</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%">
</state>
@ -490,24 +490,36 @@
<label>@text/channel-type.shelly.meterAccuWatts.label</label>
<description>@text/channel-type.shelly.meterAccuWatts.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%">
</state>
</channel-type>
<channel-type id="meterAccuTotal" advanced="true">
<item-type>Number:Power</item-type>
<item-type>Number:Energy</item-type>
<label>@text/channel-type.shelly.meterAccuTotal.label</label>
<description>@text/channel-type.shelly.meterAccuTotal.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state readOnly="true" pattern="%.3f %unit%">
</state>
</channel-type>
<channel-type id="meterAccuReturned" advanced="true">
<item-type>Number:Power</item-type>
<item-type>Number:Energy</item-type>
<label>@text/channel-type.shelly.meterAccuReturned.label</label>
<description>@text/channel-type.shelly.meterAccuReturned.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state readOnly="true" pattern="%.3f %unit%">
</state>
</channel-type>
@ -521,10 +533,14 @@
</channel-type>
<channel-type id="lastPower1" advanced="true">
<item-type>Number:Energy</item-type>
<item-type>Number:Power</item-type>
<label>@text/channel-type.shelly.lastPower1.label</label>
<description>@text/channel-type.shelly.lastPower1.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
</tags>
<state readOnly="true" pattern="%.3f %unit%">
</state>
</channel-type>
@ -533,7 +549,6 @@
<item-type>Number:Energy</item-type>
<label>@text/channel-type.shelly.meterTotal.label</label>
<description>@text/channel-type.shelly.meterTotal.description</description>
<category>Energy</category>
<state readOnly="true" pattern="%.3f %unit%">
</state>
</channel-type>
@ -543,6 +558,10 @@
<label>@text/channel-type.shelly.meterReturned.label</label>
<description>@text/channel-type.shelly.meterReturned.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state readOnly="true" pattern="%.3f %unit%">
</state>
</channel-type>
@ -551,6 +570,7 @@
<item-type>Number:ElectricPotential</item-type>
<label>@text/channel-type.shelly.meterVoltage.label</label>
<description>@text/channel-type.shelly.meterVoltage.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Voltage</tag>
@ -560,13 +580,13 @@
</channel-type>
<channel-type id="meterCurrent">
<item-type>Number:ElectricPotential</item-type>
<item-type>Number:ElectricCurrent</item-type>
<label>@text/channel-type.shelly.meterCurrent.label</label>
<description>@text/channel-type.shelly.meterCurrent.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Power</tag>
<tag>Current</tag>
</tags>
<state readOnly="true" pattern="%.3f %unit%">
</state>

View File

@ -290,4 +290,18 @@
<config-description-ref uri="thing-type:shelly:relay-gen2"/>
</thing-type>
<thing-type id="shellypluswdus">
<label>Shelly Plus Dimmer US</label>
<description>@text/thing-type.shelly.shellypluswdus.description</description>
<category>DimmableLight</category>
<channel-groups>
<channel-group id="relay" typeId="dimmerChannel"/>
<channel-group id="meter" typeId="meter"/>
<channel-group id="device" typeId="deviceStatus"/>
</channel-groups>
<representation-property>serviceName</representation-property>
<config-description-ref uri="thing-type:shelly:dimmer-gen2"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -32,4 +32,16 @@
<config-description-ref uri="thing-type:shelly:battery-gen2"/>
</thing-type>
<thing-type id="shellywalldisplay">
<label>Shelly Wall Display</label>
<description>@text/thing-type.shelly.shellywalldisplay.description</description>
<category>Sensor</category>
<channel-groups>
<channel-group id="sensors" typeId="sensorData"/>
<channel-group id="device" typeId="deviceStatus"/>
</channel-groups>
<representation-property>serviceName</representation-property>
<config-description-ref uri="thing-type:shelly:relay-gen2"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -1,6 +1,7 @@
/*
* This script uses the BLE scan functionality in scripting to pass scan reults to openHAB
* Supported BLU Devices: SBBT , SBDW
* Version 0.2
*/
let ALLTERCO_DEVICE_NAME_PREFIX = ["SBBT", "SBDW"];
@ -147,21 +148,11 @@ function scanCB(ev, res) {
}
}
// retry several times to start the scanner if script was started before
// BLE infrastructure was up in the Shelly
function startBLEScan() {
let bleScanSuccess = BLE.Scanner.Start({ duration_ms: SCAN_DURATION, active: true }, scanCB);
if( bleScanSuccess === false ) {
Timer.set(1000, false, startBLEScan);
} else {
console.log('Success: OH-BLU Event Gateway running');
}
}
let BLEConfig = Shelly.getComponentConfig('ble');
if(BLEConfig.enable === false) {
console.log('Error: BLE not enabled');
console.log('Error: BLE not enabled, unable to start OH-BLU Scanner');
} else {
Timer.set(1000, false, startBLEScan);
BLE.Scanner.Start({ duration_ms: SCAN_DURATION, active: true }, scanCB);
console.log('OH-BLU Event Gateway running');
}