[shelly] Support for Plus Smoke, Plus Plug-S/IT/UK/US, Plus Dimmer US, Pro 3EM; fix Gen1 sensor initialization (#14532)

* support for Pro 3EM (WIP)
* Support for Plug-S and Smoke added
* new channel resetTotals for emeters, new channel sensor#mute for Smoke
* Validate Temp reported by CoAP before updating channel, ignore 999
* Support device temp for Pro3, fix emeter.current rouding on 1 instead of
3 deciaml digits, check for reinit before executing channel command,
avoid NPE in Shelly Manaher
* Fix NPE in Shelly Manager with Plus/Pro devices (not having all Gen1
settings initialized)
* Avoid NPE if device time is not set; check thing stopping state to
prevent "handler already disposed"

Signed-off-by: Markus Michels <markus7017@gmail.com>
pull/15035/head
Markus Michels 2023-05-24 14:20:11 +02:00 committed by GitHub
parent df9c270acf
commit 1f774db959
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 568 additions and 116 deletions

View File

@ -76,27 +76,33 @@ The binding provides the same feature set across all devices as good as possible
### Generation 2 Plus series
| thing-type | Model | Vendor ID |
| -------------------- | -------------------------------------------------------- | ------------- |
| shellyplus1 | Shelly Plus 1 with 1x relay | SNSW-001X16EU |
| shellyplus1pm | Shelly Plus 1PM with 1x relay + power meter | SNSW-001P16EU |
| shellyplus2pm-relay | Shelly Plus 2PM with 2x relay + power meter, relay mode | SNSW-002P16EU |
| shellyplus2pm-roller | Shelly Plus 2PM with 2x relay + power meter, roller mode | SNSW-002P16EU |
| shellyplusi4 | Shelly Plus i4 with 4x AC input | SNSN-0024X |
| shellyplusi4dc | Shelly Plus i4 with 4x DC input | SNSN-0D24X |
| shellyplusht | Shelly Plus HT with temperature + humidity sensor | SNSN-0013A |
| thing-type | Model | Vendor ID |
| -------------------- | -------------------------------------------------------- | --------------------------------------------- |
| shellyplus1 | Shelly Plus 1 with 1x relay | SNSW-001X16EU |
| shellyplus1pm | Shelly Plus 1PM with 1x relay + power meter | SNSW-001P16EU |
| shellyplus2pm-relay | Shelly Plus 2PM with 2x relay + power meter, relay mode | SNSW-002P16EU, SNSW-102P16EU |
| shellyplus2pm-roller | Shelly Plus 2PM with 2x relay + power meter, roller mode | SNSW-002P16EU, SNSW-102P16EU |
| shellyplusplug | Shelly Plug-S | SNPL-00112EU |
| shellyplusplug | Shelly Plug-IT | SNPL-00110IT |
| shellyplusplug | Shelly Plug-UK | SNPL-00112UK |
| shellyplusplug | Shelly Plug-US | SNPL-00116US |
| shellyplusi4 | Shelly Plus i4 with 4x AC input | SNSN-0024X |
| 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 |
### Generation 2 Pro series
| thing-type | Model | Vendor ID |
| ------------------- | -------------------------------------------------------- | -------------- |
| shellypro1 | Shelly Pro 1 with 1x relay | SPSW-001XE16EU |
| shellypro1pm | Shelly Pro 1 PM with 1x relay + power meter | SPSW-001PE16EU |
| shellypro2-relay | Shelly Pro 2 with 2x relay, relay mode | SPSW-002XE16EU |
| shellypro2pm-relay | Shelly Pro 2 PM with 2x relay + power meter, relay mode | SPSW-002PE16EU |
| shellypro2pm-roller | Shelly Pro 2 PM with 2x relay + power meter, roller mode | SPSW-002PE16EU |
| shellypro3 | Shelly Pro 3 with 3x relay (dry contacts) | SPSW-003XE16EU |
| shellypro4pm | Shelly Pro 4 PM with 4x relay + power meter | SPSW-004PE16EU |
| thing-type | Model | Vendor ID |
| ------------------- | -------------------------------------------------------- | ---------------------------------------------- |
| shellypro1 | Shelly Pro 1 with 1x relay | SPSW-001XE16EU, SPSW-101XE16EU, SPSW-201XE16EU |
| shellypro1pm | Shelly Pro 1 PM with 1x relay + power meter | SPSW-001PE16EU, SPSW-101PE16EU, SPSW-201PE16EU |
| shellypro2-relay | Shelly Pro 2 with 2x relay, relay mode | SPSW-002XE16EU, SPSW-102XE16EU, SPSW-202XE16EU |
| shellypro2pm-relay | Shelly Pro 2 PM with 2x relay + power meter, relay mode | SPSW-002PE16EU, SPSW-102PE16EU, SPSW-202PE16EU |
| shellypro2pm-roller | Shelly Pro 2 PM with 2x relay + power meter, roller mode | SPSW-002PE16EU, SPSW-102PE16EU, SPSW-202PE16EU |
| shellypro3 | Shelly Pro 3 with 3x relay (dry contacts) | SPSW-003XE16EU |
| shellypro3em | Shelly Pro 3 with 3 integrated power meters | SPEM-003CEBEU |
| shellypro4pm | Shelly Pro 4 PM with 4x relay + power meter | SPSW-004PE16EU, SPSW-104PE16EU |
## Binding Configuration
@ -374,7 +380,7 @@ A new alarm will be triggered on a new condition or every 5 minutes if the condi
| TEMP_OVER | Above "temperature over" threshold |
| VIBRATION | A vibration/tamper was detected (DW2 only) |
Refer to section [Full Example:shelly.rules](#shellyrules) for examples how to catch alarm triggers in openHAB rules
Refer to section [Full Example](#full-example) for examples how to catch alarm triggers in openHAB rules
## Channels
@ -498,6 +504,7 @@ The Thing id is derived from the service name, so that's the reason why the Thin
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
| meter2 | currentWatts | Number | yes | Current power consumption in Watts |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
@ -506,6 +513,7 @@ The Thing id is derived from the service name, so that's the reason why the Thin
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
| meter3 | currentWatts | Number | yes | Current power consumption in Watts |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
@ -514,6 +522,7 @@ The Thing id is derived from the service name, so that's the reason why the Thin
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
### Shelly 2 - relay mode (thing-type: shelly2-relay)
@ -945,6 +954,7 @@ You should calibrate the valve using the device Web UI or Shelly App before star
| ------- | --------------- | -------- | --------- | ------------------------------------------------------------------- |
| sensors | temperature | Number | yes | Current Temperature in °C |
| | state | Contact | yes | Valve status: OPEN or CLOSED (position = 0) |
| | open | Contact | yes | ON: "window is open" was detected, OFF: window is closed |
| | lastUpdate | DateTime | yes | Timestamp of the last update (any sensor value changed) |
| control | targetTemp | Number | no | Temperature in °C: 4=Low/Min; 5..30=target temperature;31=Hi/Max |
| | position | Dimmer | no | Set valve to manual mode (0..100%) disables auto-temp) |
@ -1108,6 +1118,23 @@ If the Shelly Add-On is installed:
The roller positioning calibration has to be performed using the Shelly Web UI or App before the position can be set in percent.
Refer to [Smartify Roller Shutters with openHAB and Shelly](doc/UseCaseSmartRoller.md) for more information on roller integration.
### Shelly Plus Plug-S/IT/UK/US (thing-type: shellyplusplug)
| 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 | Sets a timer to turn the device ON after every OFF command; in seconds |
| | autoOff | Number | r/w | Sets a timer to turn the device OFF after every ON command; in seconds |
| | timerActive | Switch | yes | ON: An auto-on/off timer is active |
| | button | Trigger | yes | Event trigger, see section Button Events |
| meter | currentWatts | Number | yes | Current power consumption in Watts |
| | lastPower1 | Number | yes | Energy consumption for a round minute, 1 minute ago |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
### Shelly Plus i4, i4DC (thing-types: shellyplusi4, shellyplusi4dc)
| Group | Channel | Type | read-only | Description |
@ -1132,6 +1159,18 @@ 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 Smoke (thing-type: shellyplussmoke)
| Group | Channel | Type | read-only | Description |
| ------- | ------------ | -------- | --------- | ------------------------------------------------------- |
| sensors | smoke | Switch | yes | ON: Smoke detected |
| | mute | Switch | no | ON: Alarm muted |
| | lastUpdate | DateTime | yes | Timestamp of the last update (any sensor value changed) |
| | lastError | String | yes | Last device error. |
| battery | batteryLevel | Number | yes | Battery Level in % |
| | lowBattery | Switch | yes | Low battery alert (< 20%) |
## Shelly Pro Series
### Shelly Pro 1 (thing-type: shellypro1)
@ -1258,6 +1297,38 @@ Channels lastEvent and eventCount are only available if input type is set to mom
| | timerActive | Switch | yes | Relay #3: ON: An auto-on/off timer is active |
| | button | Trigger | yes | Relay #3: Event trigger, see section Button Events |
### Shelly Pro 3EM (thing-type: shellypro3em)
| Group | Channel | Type | read-only | Description |
| ------ | ------------- | -------- | --------- | --------------------------------------------------------------------------------- |
| meter1 | currentWatts | Number | yes | Current power consumption in Watts |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
| | returnedKWH | Number | yes | Total returned energy, kwh |
| | reactiveWatts | Number | yes | Instantaneous reactive power, Watts |
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
| meter2 | currentWatts | Number | yes | Current power consumption in Watts |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
| | returnedKWH | Number | yes | Total returned energy, kwh |
| | reactiveWatts | Number | yes | Instantaneous reactive power, Watts |
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
| meter3 | currentWatts | Number | yes | Current power consumption in Watts |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
| | returnedKWH | Number | yes | Total returned energy, kwh |
| | reactiveWatts | Number | yes | Instantaneous reactive power, Watts |
| | voltage | Number | yes | RMS voltage, Volts |
| | current | Number | yes | Current in A |
| | powerFactor | Number | yes | Power Factor in percent |
| | resetTotals | Switch | yes | ON: Resets total values for the power meter |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
### Shelly Pro 4PM (thing-type: shelly4pro)
| Group | Channel | Type | read-only | Description |

View File

@ -77,10 +77,13 @@ public class ShellyBindingConstants {
THING_TYPE_SHELLYPRO2PM_RELAY, //
THING_TYPE_SHELLYPRO2PM_ROLLER, //
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, //
THING_TYPE_SHELLYPROTECTED, //
THING_TYPE_SHELLYUNKNOWN);
@ -149,6 +152,7 @@ public class ShellyBindingConstants {
public static final String CHANNEL_EMETER_VOLTAGE = "voltage";
public static final String CHANNEL_EMETER_CURRENT = "current";
public static final String CHANNEL_EMETER_PFACTOR = "powerFactor";
public static final String CHANNEL_EMETER_RESETTOTAL = "resetTotals";
public static final String CHANNEL_GROUP_SENSOR = "sensors";
public static final String CHANNEL_SENSOR_TEMP = "temperature";
@ -161,7 +165,9 @@ public class ShellyBindingConstants {
public static final String CHANNEL_SENSOR_TILT = "tilt";
public static final String CHANNEL_SENSOR_FLOOD = "flood";
public static final String CHANNEL_SENSOR_SMOKE = "smoke";
public static final String CHANNEL_SENSOR_MUTE = "mute";
public static final String CHANNEL_SENSOR_STATE = "state";
public static final String CHANNEL_SENSOR_OPEN = "open";
public static final String CHANNEL_SENSOR_VALVE = "valve";
public static final String CHANNEL_SENSOR_SSTATE = "status"; // Shelly Gas
public static final String CHANNEL_SENSOR_MOTION_ACT = "motionActive";
@ -293,6 +299,7 @@ public class ShellyBindingConstants {
public static final int DIGITS_WATT = 2;
public static final int DIGITS_KWH = 3;
public static final int DIGITS_VOLT = 1;
public static final int DIGITS_AMPERE = 3;
public static final int DIGITS_TEMP = 1;
public static final int DIGITS_LUX = 0;
public static final int DIGITS_PERCENT = 1;

View File

@ -54,7 +54,9 @@ public interface ShellyApiInterface {
void setRelayTurn(int id, String turnMode) throws ShellyApiException;
ShellyRollerStatus getRollerStatus(int rollerIndex) throws ShellyApiException;
public void resetMeterTotal(int id) throws ShellyApiException;
public ShellyRollerStatus getRollerStatus(int rollerIndex) throws ShellyApiException;
void setRollerTurn(int relayIndex, String turnMode) throws ShellyApiException;
@ -91,6 +93,8 @@ public interface ShellyApiInterface {
void startValveBoost(int valveId, int value) throws ShellyApiException;
void muteSmokeAlarm(int smokeId) throws ShellyApiException;
ShellyOtaCheckResult checkForUpdate() throws ShellyApiException;
ShellySettingsUpdate firmwareUpdate(String uri) throws ShellyApiException;

View File

@ -99,6 +99,7 @@ public class ShellyDeviceProfile {
public boolean isButton = false; // true for a Shelly Button 1
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 int minTemp = 0; // Bulb/Duo: Min Light Temp
public int maxTemp = 0; // Bulb/Duo: Max Light Temp
@ -196,7 +197,7 @@ public class ShellyDeviceProfile {
}
boolean isFlood = thingType.equals(THING_TYPE_SHELLYFLOOD_STR);
boolean isSmoke = thingType.equals(THING_TYPE_SHELLYSMOKE_STR);
isSmoke = thingType.equals(THING_TYPE_SHELLYSMOKE_STR) || thingType.equals(THING_TYPE_SHELLYPLUSSMOKE_STR);
boolean isGas = thingType.equals(THING_TYPE_SHELLYGAS_STR);
boolean isUNI = thingType.equals(THING_TYPE_SHELLYUNI_STR);
isHT = thingType.equals(THING_TYPE_SHELLYHT_STR) || thingType.equals(THING_TYPE_SHELLYPLUSHT_STR);

View File

@ -124,7 +124,7 @@ public class Shelly1ApiJsonDTO {
//
// API values
//
public static final double SHELLY_API_INVTEMP = -999.0;
public static final double SHELLY_API_INVTEMP = 999.0;
public static final String SHELLY_BTNT_MOMENTARY = "momentary";
public static final String SHELLY_BTNT_MOM_ON_RELEASE = "momentary_on_release";
@ -920,6 +920,8 @@ public class Shelly1ApiJsonDTO {
public ShellyThermTemp tmp;
@SerializedName("boost_minutes")
public Integer boostMinutes;
@SerializedName("window_open")
public Boolean windowOpen;
}
public static class ShellySensorTmp {
@ -1103,6 +1105,7 @@ public class Shelly1ApiJsonDTO {
public ShellySensorState sensor;
public Boolean smoke; // SHelly Smoke
public Boolean flood; // Shelly Flood: true = flood condition detected
public Boolean mute; // mute enabled/disabled
@SerializedName("rain_sensor")
public Boolean rainSensor; // Shelly Flood: true=in rain mode

View File

@ -207,11 +207,11 @@ public class Shelly1CoIoTProtocol {
// event count
updateChannel(updates, group, CHANNEL_STATUS_EVENTCOUNT + profile.getInputSuffix(idx), getDecimal(count));
logger.trace(
"{}: Check button[{}] for event trigger (isButtonMode={}, isButton={}, hasBattery={}, serial={}, count={}, lastEventCount[{}]={}",
"{}: Check button[{}] for event trigger (inButtonMode={}, isButton={}, hasBattery={}, serial={}, count={}, lastEventCount[{}]={}",
thingName, idx, profile.inButtonMode(idx), profile.isButton, profile.hasBattery, serial, count, idx,
lastEventCount[idx]);
if (profile.inButtonMode(idx) && ((profile.hasBattery && (count == 1))
|| ((lastEventCount[idx] != -1) && (count != lastEventCount[idx])))) {
if (profile.inButtonMode(idx) && ((profile.hasBattery && count == 1)
|| (lastEventCount[idx] != -1 && count != lastEventCount[idx]))) {
if (!profile.isButton || (profile.isButton && (serial != 0x200))) { // skip duplicate on wake-up
logger.debug("{}: Trigger event {}", thingName, inputEvent[idx]);
thingHandler.triggerButton(group, idx, inputEvent[idx]);

View File

@ -93,8 +93,10 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
// Special handling for TRV, because it uses duplicate ID values with different meanings
switch (sen.id) {
case "3101": // current temp
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
toQuantityType(value, DIGITS_TEMP, SIUnits.CELSIUS));
if (value != SHELLY_API_INVTEMP) {
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
toQuantityType(value, DIGITS_TEMP, SIUnits.CELSIUS));
}
break;
case "3103": // target temp in C. 4/31, 999=unknown
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SETTEMP,
@ -196,7 +198,7 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
case "3201": // sensor_1: T, extTemp, C, -55/125; unknown 999
case "3301": // sensor_2: T, extTemp, C, -55/125; unknown 999
int idx = getExtTempId(sen.id);
if (idx >= 0) {
if (idx >= 0 && value != SHELLY_API_INVTEMP) {
// H&T, Fllod, DW only have 1 channel, 1/1PM with Addon have up to to 3 sensors
String channel = profile.isSensor ? CHANNEL_SENSOR_TEMP : CHANNEL_SENSOR_TEMP + idx;
// Some devices report values = -999 or 99 during fw update

View File

@ -208,6 +208,11 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
logger.debug("{}: CoIoT Message from {} (MID={}): {}", thingName,
response.getSourceContext().getPeerAddress(), response.getMID(), response.getPayloadString());
}
if (thingHandler.isStopping()) {
logger.debug("{}: Thing is shutting down, ignore CoIOT message", thingName);
return;
}
if (response.isCanceled() || response.isDuplicate() || response.isRejected()) {
logger.debug("{} ({}): Packet was canceled, rejected or is a duplicate -> discard", thingName, devId);
thingHandler.incProtErrors();

View File

@ -172,8 +172,12 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
@Override
public void setRelayTurn(int id, String turnMode) throws ShellyApiException {
callApi(getControlUriPrefix(id) + "?" + SHELLY_LIGHT_TURN + "=" + turnMode.toLowerCase(),
ShellyShortLightStatus.class);
callApi(getControlUriPrefix(id) + "?" + SHELLY_LIGHT_TURN + "=" + turnMode.toLowerCase(), String.class);
}
@Override
public void resetMeterTotal(int id) throws ShellyApiException {
callApi(SHELLY_URL_STATUS_EMETER + "/" + id + "/reset_totals=1", ShellyStatusRelay.class);
}
@Override
@ -531,6 +535,11 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
}
}
@Override
public void muteSmokeAlarm(int id) throws ShellyApiException {
throw new ShellyApiException("Request not supported");
}
/**
* Set sensor Action URLs
*

View File

@ -57,8 +57,10 @@ import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceC
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceConfig.Shelly2GetConfigResult;
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.Shelly2DeviceStatusPower;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusSmoke;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2DeviceStatusResult.Shelly2DeviceStatusTempId;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2DeviceStatus.Shelly2InputStatus;
import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2RelayStatus;
@ -175,11 +177,16 @@ public class Shelly2ApiClient extends ShellyHttpClient {
boolean channelUpdate) throws ShellyApiException {
boolean updated = false;
if (result.temperature0 != null && !getProfile().isSensor) {
status.temperature = status.tmp.tC = result.temperature0.tC;
}
updated |= updateInputStatus(status, result, channelUpdate);
updated |= updateRelayStatus(status, result.switch0, channelUpdate);
updated |= updateRelayStatus(status, result.switch1, channelUpdate);
updated |= updateRelayStatus(status, result.switch2, channelUpdate);
updated |= updateRelayStatus(status, result.switch3, channelUpdate);
updated |= updateEmStatus(status, result.em0, channelUpdate);
updated |= updateRollerStatus(status, result.cover0, channelUpdate);
if (channelUpdate) {
updated |= ShellyComponents.updateMeters(getThing(), status);
@ -187,6 +194,7 @@ public class Shelly2ApiClient extends ShellyHttpClient {
updateHumidityStatus(sensorData, result.humidity0);
updateTemperatureStatus(sensorData, result.temperature0);
updateSmokeStatus(sensorData, result.smoke0);
updateBatteryStatus(sensorData, result.devicepower0);
updateAddonStatus(status, result);
updated |= ShellyComponents.updateSensors(getThing(), status);
@ -263,14 +271,91 @@ public class Shelly2ApiClient extends ShellyHttpClient {
// Update internal structures
status.relays.set(rs.id, rstatus);
status.meters.set(rs.id, sm);
status.emeters.set(rs.id, emeter);
relayStatus.relays.set(rs.id, sr);
relayStatus.meters.set(rs.id, sm);
updateMeter(status, rs.id, sm, emeter, channelUpdate);
return channelUpdate ? ShellyComponents.updateRelay((ShellyBaseHandler) getThing(), status, rs.id) : false;
}
private void updateMeter(ShellySettingsStatus status, int id, ShellySettingsMeter sm, ShellySettingsEMeter emeter,
boolean channelUpdate) throws ShellyApiException {
status.meters.set(id, sm);
status.emeters.set(id, emeter);
relayStatus.meters.set(id, sm);
}
private boolean updateEmStatus(ShellySettingsStatus status, @Nullable Shelly2DeviceStatusEm em,
boolean channelUpdate) throws ShellyApiException {
if (em == null) {
return false;
}
boolean updated = false;
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;
}
if (em.aAprtPower != null) {
emeter.totalReturned = em.aAprtPower;
}
if (em.aVoltage != null) {
emeter.voltage = em.aVoltage;
}
if (em.aCurrent != null) {
emeter.current = em.aCurrent;
}
if (em.aPF != null) {
emeter.pf = em.aPF;
}
// Update internal structures
updateMeter(status, 0, sm, emeter, channelUpdate);
sm = new ShellySettingsMeter();
emeter = status.emeters.get(1);
sm.isValid = emeter.isValid = true;
if (em.bActPower != null) {
sm.power = emeter.power = em.bActPower;
}
if (em.bAprtPower != null) {
emeter.totalReturned = em.bAprtPower;
}
if (em.bVoltage != null) {
emeter.voltage = em.bVoltage;
}
if (em.bCurrent != null) {
emeter.current = em.bCurrent;
}
if (em.bPF != null) {
emeter.pf = em.bPF;
}
// Update internal structures
updateMeter(status, 1, sm, emeter, channelUpdate);
sm = new ShellySettingsMeter();
emeter = status.emeters.get(2);
sm.isValid = emeter.isValid = true;
if (em.cActPower != null) {
sm.power = emeter.power = em.cActPower;
}
if (em.cAprtPower != null) {
emeter.totalReturned = em.cAprtPower;
}
if (em.cVoltage != null) {
emeter.voltage = em.cVoltage;
}
if (em.cCurrent != null) {
emeter.current = em.cCurrent;
}
if (em.cPF != null) {
emeter.pf = em.cPF;
}
// Update internal structures
updateMeter(status, 2, sm, emeter, channelUpdate);
return channelUpdate ? ShellyComponents.updateMeters(getThing(), status) : false;
}
protected @Nullable ArrayList<@Nullable ShellySettingsRoller> fillRollerSettings(ShellyDeviceProfile profile,
Shelly2GetConfigResult dc) {
if (dc.cover0 == null) {
@ -360,7 +445,9 @@ public class Shelly2ApiClient extends ShellyHttpClient {
if (cs.aenergy != null) {
sm.total = emeter.total = cs.aenergy.total;
sm.counters = cs.aenergy.byMinute;
sm.timestamp = (long) cs.aenergy.minuteTs;
if (cs.aenergy.minuteTs != null) {
sm.timestamp = (long) cs.aenergy.minuteTs;
}
}
if (cs.voltage != null) {
emeter.voltage = cs.voltage;
@ -448,6 +535,14 @@ public class Shelly2ApiClient extends ShellyHttpClient {
sdata.tmp.tF = value.tF;
}
protected void updateSmokeStatus(ShellyStatusSensor sdata, @Nullable Shelly2DeviceStatusSmoke value) {
if (value == null) {
return;
}
sdata.smoke = getBool(value.alarm);
sdata.mute = getBool(value.mute);
}
protected void updateBatteryStatus(ShellyStatusSensor sdata, @Nullable Shelly2DeviceStatusPower value) {
if (value == null) {
return;

View File

@ -56,6 +56,8 @@ 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_SMOKE_SETCONFIG = "Smoke.SetConfig";
public static final String SHELLYRPC_METHOD_SMOKE_MUTE = "Smoke.Mute";
public static final String SHELLYRPC_METHOD_NOTIFYSTATUS = "NotifyStatus"; // inbound status
public static final String SHELLYRPC_METHOD_NOTIFYFULLSTATUS = "NotifyFullStatus"; // inbound status from bat device
@ -283,6 +285,17 @@ public class Shelly2ApiJsonDTO {
public Double currentLimit;
}
public static class Shelly2DevConfigEm {
public Integer id;
public String name;
@SerializedName("blink_mode_selector")
public String blinkModeSelector;
@SerializedName("phase_selector")
public String phase_selector;
@SerializedName("monitor_phase_sequence")
public Boolean monitorPhaseSequence;
}
public class Shelly2DevConfigCover {
public class Shelly2DeviceConfigCoverMotor {
@SerializedName("idle_power_thr")
@ -333,6 +346,12 @@ public class Shelly2ApiJsonDTO {
public Shelly2DeviceConfigCoverObstructionDetection obstructionDetection;
}
public static class Shelly2ConfigSmoke {
public Integer id;
public Boolean alarm;
public Boolean mute;
}
public static class Shelly2GetConfigResult {
public class Shelly2DevConfigCloud {
@ -377,8 +396,14 @@ public class Shelly2ApiJsonDTO {
@SerializedName("switch:3")
public Shelly2DevConfigSwitch switch3;
@SerializedName("em:0")
public Shelly2DevConfigEm em0;
@SerializedName("cover:0")
public Shelly2DevConfigCover cover0;
@SerializedName("smoke:0")
public Shelly2ConfigSmoke smoke0;
}
public class Shelly2DeviceConfigSta {
@ -486,6 +511,57 @@ public class Shelly2ApiJsonDTO {
public Shelly2DeviceStatusCharger external;
}
public static class Shelly2DeviceStatusEm {
public Integer id;
@SerializedName("a_current")
public Double aCurrent;
@SerializedName("a_voltage")
public Double aVoltage;
@SerializedName("a_act_power")
public Double aActPower;
@SerializedName("a_aprt_power")
public Double aAprtPower;
@SerializedName("a_pf")
public Double aPF;
@SerializedName("b_current")
public Double bCurrent;
@SerializedName("b_voltage")
public Double bVoltage;
@SerializedName("b_act_power")
public Double bActPower;
@SerializedName("b_aprt_power")
public Double bAprtPower;
@SerializedName("b_pf")
public Double bPF;
@SerializedName("c_current")
public Double cCurrent;
@SerializedName("c_voltage")
public Double cVoltage;
@SerializedName("c_act_power")
public Double cActPower;
@SerializedName("c_aprt_power")
public Double cAprtPower;
@SerializedName("c_pf")
public Double cPF;
@SerializedName("n_current")
public Double nCurrent;
}
public static class Shelly2DeviceStatusEmData {
public Integer id;
public String[] errors;
}
public class Shelly2DeviceStatusSmoke {
public Integer id;
public Boolean alarm;
public Boolean mute;
}
public Shelly2DeviceStatusBle ble;
public Shelly2DeviceStatusCloud cloud;
public Shelly2DeviceStatusMqqt mqtt;
@ -512,6 +588,11 @@ public class Shelly2ApiJsonDTO {
@SerializedName("switch:3")
public Shelly2RelayStatus switch3;
@SerializedName("em:0")
Shelly2DeviceStatusEm em0;
@SerializedName("emdata:0")
Shelly2DeviceStatusEmData emdata0;
@SerializedName("cover:0")
public Shelly2CoverStatus cover0;
@ -532,6 +613,8 @@ public class Shelly2ApiJsonDTO {
public Shelly2DeviceStatusHumidity humidity0;
@SerializedName("humidity:100")
public Shelly2DeviceStatusHumidity humidity100;
@SerializedName("smoke:0")
public Shelly2DeviceStatusSmoke smoke0;
@SerializedName("voltmeter:100")
public Shelly2DeviceStatusVoltage voltmeter100;

View File

@ -205,22 +205,15 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
fillWiFiSta(dc.wifi.sta, profile.settings.wifiSta);
fillWiFiSta(dc.wifi.sta1, profile.settings.wifiSta1);
profile.numMeters = 0;
if (profile.hasRelays) {
profile.status.relays = new ArrayList<>();
profile.status.meters = new ArrayList<>();
profile.status.emeters = new ArrayList<>();
relayStatus.relays = new ArrayList<>();
relayStatus.meters = new ArrayList<>();
profile.numMeters = profile.isRoller ? profile.numRollers : profile.numRelays;
for (int i = 0; i < profile.numRelays; i++) {
profile.status.relays.add(new ShellySettingsRelay());
relayStatus.relays.add(new ShellyShortStatusRelay());
}
for (int i = 0; i < profile.numMeters; i++) {
profile.status.meters.add(new ShellySettingsMeter());
profile.status.emeters.add(new ShellySettingsEMeter());
relayStatus.meters.add(new ShellySettingsMeter());
}
}
if (profile.numInputs > 0) {
@ -236,6 +229,22 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
}
}
if (dc.em0 != null) {
profile.numMeters = 3;
}
if (profile.numMeters > 0) {
profile.status.meters = new ArrayList<>();
profile.status.emeters = new ArrayList<>();
relayStatus.meters = new ArrayList<>();
for (int i = 0; i < profile.numMeters; i++) {
profile.status.meters.add(new ShellySettingsMeter());
profile.status.emeters.add(new ShellySettingsEMeter());
relayStatus.meters.add(new ShellySettingsMeter());
}
}
if (profile.isRoller) {
profile.status.rollers = new ArrayList<>();
for (int i = 0; i < profile.numRollers; i++) {
@ -278,7 +287,8 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
private void checkSetWsCallback() throws ShellyApiException {
Shelly2ConfigParms wsConfig = apiRequest(SHELLYRPC_METHOD_WSGETCONFIG, null, Shelly2ConfigParms.class);
String url = "ws://" + config.localIp + ":" + config.localPort + "/shelly/wsevent";
if (!getBool(wsConfig.enable) || !url.equalsIgnoreCase(getString(wsConfig.server))) {
if (!config.localIp.isEmpty() && !getBool(wsConfig.enable)
|| !url.equalsIgnoreCase(getString(wsConfig.server))) {
logger.debug("{}: A battery device was detected without correct callback, fix it", thingName);
wsConfig.enable = true;
wsConfig.server = url;
@ -314,6 +324,10 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
thingName, thingName, message.src, message.dst, discovery);
return;
}
if (t.isStopping()) {
logger.debug("{}: Thing is shutting down, ignore WebSocket message", thingName);
return;
}
if (!t.isThingOnline() && t.getThingStatusDetail() != ThingStatusDetail.CONFIGURATION_PENDING) {
logger.debug("{}: Thing is not in online state/connectable, ignore NotifyStatus", thingName);
return;
@ -627,6 +641,15 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
apiRequest(req);
}
@Override
public void resetMeterTotal(int id) throws ShellyApiException {
}
@Override
public void muteSmokeAlarm(int index) throws ShellyApiException {
apiRequest(new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_SMOKE_MUTE).withId(index));
}
@Override
public ShellySettingsLogin getLoginSettings() throws ShellyApiException {
return new ShellySettingsLogin();

View File

@ -71,10 +71,14 @@ public class ShellyThingCreator {
public static final String SHELLYDT_PLUS2PM_ROLLER = "SNSW-002P16EU-roller";
public static final String SHELLYDT_PLUS2PM_RELAY_2 = "SNSW-102P16EU-relay";
public static final String SHELLYDT_PLUS2PM_ROLLER_2 = "SNSW-102P16EU-roller";
public static final String SHELLYDT_PLUSPLUGS = "SNPL-00112EU";
public static final String SHELLYDT_PLUSPLUGIT = "SNPL-00110IT";
public static final String SHELLYDT_PLUSPLUGUK = "SNPL-00112UK";
public static final String SHELLYDT_PLUSPLUGUS = "SNPL-00116US";
public static final String SHELLYDT_PLUSI4 = "SNSN-0024X";
public static final String SHELLYDT_PLUSI4DC = "SNSN-0D24X";
public static final String SHELLYDT_PLUSHT = "SNSN-0013A";
public static final String SHELLYDT_PLUSSMOKE = "SNSN-0031Z";
// Shelly Pro Series
public static final String SHELLYDT_PRO1 = "SPSW-001XE16EU";
@ -93,6 +97,7 @@ public class ShellyThingCreator {
public static final String SHELLYDT_PRO2PM_RELAY_3 = "SPSW-202PE16EU-relay";
public static final String SHELLYDT_PRO2PM_ROLLER_3 = "SPSW-202PE16EU-roller";
public static final String SHELLYDT_PRO3 = "SPSW-003XE16EU";
public static final String SHELLYDT_PRO3EM = "SPEM-003CEBEU";
public static final String SHELLYDT_PRO4PM = "SPSW-004PE16EU";
public static final String SHELLYDT_PRO4PM_2 = "SPSW-104PE16EU";
@ -145,6 +150,8 @@ public class ShellyThingCreator {
public static final String THING_TYPE_SHELLYPLUSI4_STR = "shellyplusi4";
public static final String THING_TYPE_SHELLYPLUSI4DC_STR = "shellyplusi4dc";
public static final String THING_TYPE_SHELLYPLUSHT_STR = "shellyplusht";
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";
// Shelly Pro Series
@ -154,6 +161,7 @@ public class ShellyThingCreator {
public static final String THING_TYPE_SHELLYPRO2PM_RELAY_STR = "shellypro2pm-relay";
public static final String THING_TYPE_SHELLYPRO2PM_ROLLER_STR = "shellypro2pm-roller";
public static final String THING_TYPE_SHELLYPRO3_STR = "shellypro3";
public static final String THING_TYPE_SHELLYPRO3EM_STR = "shellypro3em";
public static final String THING_TYPE_SHELLYPRO4PM_STR = "shellypro4pm";
public static final String THING_TYPE_SHELLYPROTECTED_STR = "shellydevice";
@ -229,6 +237,10 @@ public class ShellyThingCreator {
THING_TYPE_SHELLYPLUSI4DC_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSHT = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUSHT_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSSMOKE = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPLUSSMOKE_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPLUSPLUGS = new ThingTypeUID(BINDING_ID,
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_SHELLYPRO1 = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYPRO1_STR);
@ -241,6 +253,8 @@ public class ShellyThingCreator {
public static final ThingTypeUID THING_TYPE_SHELLYPRO2PM_ROLLER = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPRO2PM_ROLLER_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPRO3 = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYPRO3_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPRO3EM = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPRO3EM_STR);
public static final ThingTypeUID THING_TYPE_SHELLYPRO4PM = new ThingTypeUID(BINDING_ID,
THING_TYPE_SHELLYPRO4PM_STR);
@ -280,10 +294,14 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(SHELLYDT_PLUS2PM_ROLLER, THING_TYPE_SHELLYPLUS2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUS2PM_RELAY_2, THING_TYPE_SHELLYPLUS2PM_RELAY_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUS2PM_ROLLER_2, THING_TYPE_SHELLYPLUS2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSPLUGS, THING_TYPE_SHELLYPLUSPLUGS_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSPLUGIT, THING_TYPE_SHELLYPLUSPLUGS_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSPLUGUK, THING_TYPE_SHELLYPLUSPLUGS_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSPLUGUS, THING_TYPE_SHELLYPLUSPLUGUS_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PLUSI4DC, THING_TYPE_SHELLYPLUSI4DC_STR);
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);
// Pro Series
THING_TYPE_MAPPING.put(SHELLYDT_PRO1, THING_TYPE_SHELLYPRO1_STR);
@ -302,6 +320,7 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(SHELLYDT_PRO2PM_ROLLER_2, THING_TYPE_SHELLYPRO2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PRO2PM_ROLLER_3, THING_TYPE_SHELLYPRO2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PRO3, THING_TYPE_SHELLYPRO3_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PRO3EM, THING_TYPE_SHELLYPRO3EM_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PRO4PM, THING_TYPE_SHELLYPRO4PM_STR);
THING_TYPE_MAPPING.put(SHELLYDT_PRO4PM_2, THING_TYPE_SHELLYPRO4PM_STR);
@ -309,12 +328,12 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1_STR, THING_TYPE_SHELLY1_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1PM_STR, THING_TYPE_SHELLY1PM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1L_STR, THING_TYPE_SHELLY1L_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY3EM_STR, THING_TYPE_SHELLY3EM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYEM_STR, THING_TYPE_SHELLYEM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY4PRO_STR, THING_TYPE_SHELLY4PRO_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDIMMER2_STR, THING_TYPE_SHELLYDIMMER2_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDIMMER_STR, THING_TYPE_SHELLYDIMMER_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYIX3_STR, THING_TYPE_SHELLYIX3_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY3EM_STR, THING_TYPE_SHELLY3EM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYEM_STR, THING_TYPE_SHELLYEM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDUORGBW_STR, THING_TYPE_SHELLYDUORGBW_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDUO_STR, THING_TYPE_SHELLYDUO_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYVINTAGE_STR, THING_TYPE_SHELLYVINTAGE_STR);
@ -334,6 +353,29 @@ public class ShellyThingCreator {
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYUNI_STR, THING_TYPE_SHELLYUNI_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYMOTION2_STR, THING_TYPE_SHELLYMOTION_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS1PM_STR, THING_TYPE_SHELLYPLUS1PM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS1_STR, THING_TYPE_SHELLYPLUS1_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS1PM_STR, THING_TYPE_SHELLYPLUS1PM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS2PM_RELAY_STR, THING_TYPE_SHELLYPLUS2PM_RELAY_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS2PM_ROLLER_STR, THING_TYPE_SHELLYPLUS2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS2PM_RELAY_STR, THING_TYPE_SHELLYPLUS2PM_RELAY_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUS2PM_ROLLER_STR, THING_TYPE_SHELLYPLUS2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSPLUGS_STR, THING_TYPE_SHELLYPLUSPLUGS_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSPLUGUS_STR, THING_TYPE_SHELLYPLUSPLUGUS_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPLUSI4DC_STR, THING_TYPE_SHELLYPLUSI4DC_STR);
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_SHELLYPRO1_STR, THING_TYPE_SHELLYPRO1_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO1PM_STR, THING_TYPE_SHELLYPRO1PM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO2PM_RELAY_STR, THING_TYPE_SHELLYPRO2PM_RELAY_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO2PM_ROLLER_STR, THING_TYPE_SHELLYPRO2PM_ROLLER_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO2_RELAY_STR, THING_TYPE_SHELLYPRO2_RELAY_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO3EM_STR, THING_TYPE_SHELLYPRO3EM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO3_STR, THING_TYPE_SHELLYPRO3_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPRO4PM_STR, THING_TYPE_SHELLYPRO4PM_STR);
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPROTECTED_STR, THING_TYPE_SHELLYPROTECTED_STR);
}

View File

@ -235,7 +235,8 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
if (coap != null) {
coap.stop();
}
requestUpdates(1, true);// force re-initialization
stopping = false;
reinitializeThing();// force re-initialization
}
/**
@ -249,7 +250,6 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
*/
public boolean initializeThing() throws ShellyApiException {
// Init from thing type to have a basic profile, gets updated when device info is received from API
stopping = false;
refreshSettings = false;
lastWakeupReason = "";
cache.setThingName(thingName);
@ -263,6 +263,8 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
return false;
}
profile.initFromThingType(thingType); // do some basic initialization
// Gen 1 only: Setup CoAP listener to we get the CoAP message, which triggers initialization even the thing
// could not be fully initialized here. In this case the CoAP messages triggers auto-initialization (like the
// Action URL does when enabled)
@ -272,7 +274,6 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
// Initialize API access, exceptions will be catched by initialize()
api.initialize();
profile.initFromThingType(thingType);
ShellySettingsDevice devInfo = api.getDeviceInfo();
if (getBool(devInfo.auth) && config.password.isEmpty()) {
setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-no-credentials");
@ -357,7 +358,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
return;
}
if (!profile.isInitialized()) {
if (!profile.isInitialized() || (isThingOffline() && profile.alwaysOn)) {
logger.debug("{}: {}", thingName, messages.get("command.init", command));
initializeThing();
} else {
@ -440,7 +441,13 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
logger.debug("{}: Set boost timer to {}", thingName, command);
api.setValveBoostTime(0, (int) getNumber(command));
break;
case CHANNEL_SENSOR_MUTE:
if (profile.isSmoke && ((OnOffType) command) == OnOffType.ON) {
logger.debug("{}: Mute Smoke Alarm", thingName);
api.muteSmokeAlarm(0);
updateChannel(getString(channelUID.getGroupId()), CHANNEL_SENSOR_MUTE, OnOffType.OFF);
}
break;
default:
update = handleDeviceCommand(channelUID, command);
break;
@ -500,11 +507,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
logger.debug("{}: Status update triggered thing initialization", thingName);
initializeThing(); // may fire an exception if initialization failed
}
// Get profile, if refreshSettings == true reload settings from device
ShellySettingsStatus status = api.getStatus();
if (status.uptime != null && status.uptime == 0 && profile.alwaysOn) {
status = api.getStatus();
}
boolean restarted = checkRestarted(status);
profile = getProfile(refreshSettings || restarted);
profile.status = status;
@ -541,7 +544,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
status = "offline.conf-error-access-denied";
} else if (isWatchdogStarted()) {
if (!isWatchdogExpired()) {
logger.debug("{}: Ignore API Timeout, retry later", thingName);
logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
} else {
if (isThingOnline()) {
status = "offline.status-error-watchdog";
@ -692,7 +695,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
@Override
public void reinitializeThing() {
logger.debug("{}: Re-Initialize Thing", thingName);
if (stopping) {
if (isStopping()) {
logger.debug("{}: Handler is shutting down, ignore", thingName);
return;
}
@ -701,6 +704,11 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
requestUpdates(0, true);
}
@Override
public boolean isStopping() {
return stopping;
}
@Override
public void fillDeviceStatus(ShellySettingsStatus status, boolean updated) {
String alarm = "";

View File

@ -27,10 +27,9 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettings
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
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.OpenClosedType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
@ -113,33 +112,17 @@ public class ShellyComponents {
if (status.extSwitch != null) {
if (status.extSwitch.input0 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_INPUT1,
getInteger(status.extSwitch.input0.input) == 1 ? OpenClosedType.OPEN
: OpenClosedType.CLOSED);
getOpenClosed(getInteger(status.extSwitch.input0.input) == 1));
}
}
if (status.extTemperature != null) {
// Shelly 1/1PM support up to 3 external sensors
// for whatever reason those are not represented as an array, but 3 elements
if (status.extTemperature.sensor1 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_TEMP1,
toQuantityType(getDouble(status.extTemperature.sensor1.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor2 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_TEMP2,
toQuantityType(getDouble(status.extTemperature.sensor2.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor3 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_TEMP3,
toQuantityType(getDouble(status.extTemperature.sensor3.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor4 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_TEMP4,
toQuantityType(getDouble(status.extTemperature.sensor4.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor5 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_TEMP5,
toQuantityType(getDouble(status.extTemperature.sensor5.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
updated |= updateTempChannel(status.extTemperature.sensor1, thingHandler, CHANNEL_ESENSOR_TEMP1);
updated |= updateTempChannel(status.extTemperature.sensor2, thingHandler, CHANNEL_ESENSOR_TEMP2);
updated |= updateTempChannel(status.extTemperature.sensor3, thingHandler, CHANNEL_ESENSOR_TEMP3);
updated |= updateTempChannel(status.extTemperature.sensor4, thingHandler, CHANNEL_ESENSOR_TEMP4);
updated |= updateTempChannel(status.extTemperature.sensor5, thingHandler, CHANNEL_ESENSOR_TEMP5);
}
if ((status.extHumidity != null) && (status.extHumidity.sensor1 != null)) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENSOR_HUMIDITY,
@ -277,7 +260,7 @@ public class ShellyComponents {
String groupName = profile.getMeterGroup(m);
if (!thingHandler.areChannelsCreated()) {
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions
.createEMeterChannels(thingHandler.getThing(), emeter, groupName));
.createEMeterChannels(thingHandler.getThing(), profile, emeter, groupName));
}
// convert Watt/Hour tok w/h
@ -292,7 +275,7 @@ public class ShellyComponents {
updated |= thingHandler.updateChannel(groupName, CHANNEL_EMETER_VOLTAGE,
toQuantityType(getDouble(emeter.voltage), DIGITS_VOLT, Units.VOLT));
updated |= thingHandler.updateChannel(groupName, CHANNEL_EMETER_CURRENT,
toQuantityType(getDouble(emeter.current), DIGITS_VOLT, Units.AMPERE));
toQuantityType(getDouble(emeter.current), DIGITS_AMPERE, Units.AMPERE));
updated |= thingHandler.updateChannel(groupName, CHANNEL_EMETER_PFACTOR,
toQuantityType(computePF(emeter), Units.PERCENT));
@ -404,8 +387,7 @@ public class ShellyComponents {
if ((sdata.sensor != null) && sdata.sensor.isValid) {
// Shelly DW: “sensor”:{“state”:“open”, “is_valid”:true},
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE,
getString(sdata.sensor.state).equalsIgnoreCase(SHELLY_API_DWSTATE_OPEN) ? OpenClosedType.OPEN
: OpenClosedType.CLOSED);
getOpenClosed(getString(sdata.sensor.state).equalsIgnoreCase(SHELLY_API_DWSTATE_OPEN)));
String sensorError = sdata.sensorError;
boolean changed = thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR,
getStringType(sensorError));
@ -414,17 +396,12 @@ public class ShellyComponents {
}
updated |= changed;
}
if ((sdata.tmp != null) && getBool(sdata.tmp.isValid)) {
if (sdata.tmp != null && getBool(sdata.tmp.isValid)) {
Double temp = getString(sdata.tmp.units).toUpperCase().equals(SHELLY_TEMP_CELSIUS)
? getDouble(sdata.tmp.tC)
: getDouble(sdata.tmp.tF);
if (getString(sdata.tmp.units).toUpperCase().equals(SHELLY_TEMP_FAHRENHEIT)) {
// convert Fahrenheit to Celsius
temp = ImperialUnits.FAHRENHEIT.getConverterTo(SIUnits.CELSIUS).convert(temp).doubleValue();
}
temp = convertToC(temp, getString(sdata.tmp.units));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
toQuantityType(temp.doubleValue(), DIGITS_TEMP, SIUnits.CELSIUS));
updated |= updateTempChannel(thingHandler, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
temp.doubleValue(), getString(sdata.tmp.units));
} else if (status.thermostats != null) {
// Shelly TRV
if (profile.settings.thermostats != null) {
@ -438,24 +415,25 @@ public class ShellyComponents {
toQuantityType((double) bminutes, DIGITS_NONE, Units.MINUTE));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE, getStringType(
getBool(t.targetTemp.enabled) ? SHELLY_TRV_MODE_AUTO : SHELLY_TRV_MODE_MANUAL));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_OPEN,
getOpenClosed(t.windowOpen));
int pid = getBool(t.schedule) ? getInteger(t.profile) : 0;
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE,
getOnOff(t.schedule));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
getStringType(profile.getValueProfile(0, pid)));
if (t.tmp != null) {
Double temp = convertToC(t.tmp.value, getString(t.tmp.units));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
toQuantityType(temp.doubleValue(), DIGITS_TEMP, SIUnits.CELSIUS));
temp = convertToC(t.targetTemp.value, getString(t.targetTemp.unit));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SETTEMP,
toQuantityType(t.targetTemp.value, DIGITS_TEMP, SIUnits.CELSIUS));
updated |= updateTempChannel(thingHandler, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
t.tmp.value, t.tmp.units);
updated |= updateTempChannel(thingHandler, CHANNEL_GROUP_SENSOR, CHANNEL_CONTROL_SETTEMP,
t.targetTemp.value, t.targetTemp.unit);
}
if (t.pos != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_POSITION,
t.pos != -1 ? toQuantityType(t.pos, DIGITS_NONE, Units.PERCENT) : UnDefType.UNDEF);
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE,
getDouble(t.pos) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
getOpenClosed(getDouble(t.pos) > 0));
}
}
}
@ -485,6 +463,10 @@ public class ShellyComponents {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_SMOKE,
getOnOff(sdata.smoke));
}
if (sdata.mute != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MUTE, getOnOff(sdata.mute));
}
if (sdata.gasSensor != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SELFTTEST,
getStringType(sdata.gasSensor.selfTestState));
@ -506,7 +488,7 @@ public class ShellyComponents {
boolean charger = (getInteger(profile.settings.externalPower) == 1) || getBool(sdata.charger);
if ((profile.settings.externalPower != null) || (sdata.charger != null)) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_CHARGER,
charger ? OnOffType.ON : OnOffType.OFF);
getOnOff(charger));
}
if (sdata.bat != null) { // no update for Sense
updated |= thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL,
@ -514,7 +496,7 @@ public class ShellyComponents {
int lowBattery = thingHandler.getThingConfig().lowBattery;
boolean changed = thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW,
!charger && getDouble(sdata.bat.value) < lowBattery ? OnOffType.ON : OnOffType.OFF);
getOnOff(!charger && getDouble(sdata.bat.value) < lowBattery));
updated |= changed;
if (!charger && changed && getDouble(sdata.bat.value) < lowBattery) {
thingHandler.postEvent(ALARM_TYPE_LOW_BATTERY, false);
@ -546,11 +528,25 @@ public class ShellyComponents {
return updated;
}
private static Double convertToC(@Nullable Double temp, String unit) {
if (temp == null) {
return 0.0;
public static boolean updateTempChannel(@Nullable ShellyShortTemp sensor, ShellyThingInterface thingHandler,
String channel) {
return sensor != null ? updateTempChannel(thingHandler, CHANNEL_GROUP_SENSOR, channel, sensor.tC, "") : false;
}
public static boolean updateTempChannel(ShellyThingInterface thingHandler, String group, String channel,
@Nullable Double temp, @Nullable String unit) {
if (temp == null || temp == SHELLY_API_INVTEMP) {
return false;
}
if (SHELLY_TEMP_FAHRENHEIT.equalsIgnoreCase(unit)) {
return thingHandler.updateChannel(group, channel,
toQuantityType(convertToC(temp, unit), DIGITS_TEMP, SIUnits.CELSIUS));
}
private static Double convertToC(@Nullable Double temp, @Nullable String unit) {
if (temp == null || temp == SHELLY_API_INVTEMP) {
return SHELLY_API_INVTEMP;
}
if (SHELLY_TEMP_FAHRENHEIT.equalsIgnoreCase(getString(unit))) {
// convert Fahrenheit to Celsius
return ImperialUnits.FAHRENHEIT.getConverterTo(SIUnits.CELSIUS).convert(temp).doubleValue();
}

View File

@ -137,6 +137,12 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
logger.debug("{}: Set Auto-OFF timer to {}", thingName, command);
api.setAutoTimer(rIndex, SHELLY_TIMER_AUTOOFF, getNumber(command).doubleValue());
break;
case CHANNEL_EMETER_RESETTOTAL:
logger.debug("{}: Reset Meter Totals", thingName);
int mIndex = Integer.parseInt(substringAfter(groupName, CHANNEL_GROUP_METER)) - 1;
api.resetMeterTotal(mIndex);
updateChannel(groupName, CHANNEL_EMETER_RESETTOTAL, OnOffType.OFF);
break;
}
return true;
}

View File

@ -54,6 +54,8 @@ public interface ShellyThingInterface {
void setThingOffline(ThingStatusDetail detail, String messageKey, Object... arguments);
boolean isStopping();
String getThingType();
ThingStatus getThingStatus();

View File

@ -381,18 +381,18 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
list.put(ACTION_PROTECT, "Protect Device");
}
if ((profile.settings.coiot != null) && profile.settings.coiot.peer != null) {
if (profile.settings.coiot != null && profile.settings.coiot.peer != null) {
boolean mcast = profile.settings.coiot.peer.isEmpty()
|| SHELLY_COIOT_MCAST.equalsIgnoreCase(profile.settings.coiot.peer) || profile.isMotion;
list.put(mcast ? ACTION_SETCOIOT_PEER : ACTION_SETCOIOT_MCAST,
mcast ? "Set CoIoT Peer Mode" : "Set CoIoT Multicast Mode");
}
if (profile.isSensor && !profile.isMotion && profile.settings.wifiSta != null
&& profile.settings.wifiSta.enabled) {
&& getBool(profile.settings.wifiSta.enabled)) {
// FW 1.10+: Reset STA list, force WiFi rescan and connect to stringest AP
list.put(ACTION_RESSTA, "Reconnect WiFi");
}
if (!gen2 && profile.settings.apRoaming != null) {
if (!gen2 && profile.settings.apRoaming != null && profile.settings.apRoaming.enabled != null) {
list.put(!profile.settings.apRoaming.enabled ? ACTION_ENAPROAMING : ACTION_DISAPROAMING,
!profile.settings.apRoaming.enabled ? "Enable WiFi Roaming" : "Disable WiFi Roaming");
}
@ -413,7 +413,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
!profile.settings.bluetooth ? "Enable Bluetooth" : "Disable Bluetooth");
}
boolean set = profile.settings.cloud != null && profile.settings.cloud.enabled;
boolean set = profile.settings.cloud != null && getBool(profile.settings.cloud.enabled);
list.put(set ? ACTION_DISCLOUD : ACTION_ENCLOUD, set ? "Disable Cloud" : "Enable Cloud");
list.put(ACTION_RESET, "-Factory Reset");

View File

@ -199,6 +199,7 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_VOLTAGE, "meterVoltage", ITEMT_VOLT))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_CURRENT, "meterCurrent", ITEMT_AMP))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEMT_NUMBER))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_RESETTOTAL, "meterResetTotals", ITEMT_SWITCH))
// Sensors
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEMT_TEMP))
@ -207,6 +208,7 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ILLUM, "sensorIllumination", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VOLTAGE, "sensorADC", ITEMT_VOLT))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_STATE, "sensorContact", ITEMT_CONTACT))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_OPEN, "sensorOpen", ITEMT_CONTACT))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEMT_ANGLE))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEMT_SWITCH))
@ -215,6 +217,7 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "vibration", 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))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_PPM, "sensorPPM", ITEMT_NUMBER))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VALVE, "sensorValve", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ALARM_STATE, "alarmState", ITEMT_STRING))
@ -455,8 +458,8 @@ public class ShellyChannelDefinitions {
return newChannels;
}
public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellySettingsEMeter emeter,
String group) {
public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellyDeviceProfile profile,
final ShellySettingsEMeter emeter, String group) {
Map<String, Channel> newChannels = new LinkedHashMap<>();
addChannel(thing, newChannels, emeter.power != null, group, CHANNEL_METER_CURRENTWATTS);
addChannel(thing, newChannels, emeter.total != null, group, CHANNEL_METER_TOTALKWH);
@ -465,7 +468,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, true, group, CHANNEL_LAST_UPDATE);
return newChannels;
}
@ -482,7 +485,8 @@ public class ShellyChannelDefinitions {
addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
CHANNEL_SENSOR_ILLUM);
addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_SMOKE);
addChannel(thing, newChannels, sdata.mute != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MUTE);
addChannel(thing, newChannels, profile.settings.externalPower != null || sdata.charger != null, CHGR_DEVST,
CHANNEL_DEVST_CHARGER);
addChannel(thing, newChannels, sdata.motion != null || (sdata.sensor != null && sdata.sensor.motion != null),
@ -528,6 +532,7 @@ public class ShellyChannelDefinitions {
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_OPEN);
}
// Battery

View File

@ -37,6 +37,7 @@ import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
@ -245,7 +246,11 @@ public class ShellyUtils {
}
public static OnOffType getOnOff(@Nullable Boolean value) {
return (value != null ? value ? OnOffType.ON : OnOffType.OFF : OnOffType.OFF);
return (value != null && value ? OnOffType.ON : OnOffType.OFF);
}
public static OpenClosedType getOpenClosed(@Nullable Boolean value) {
return (value != null && value ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
}
public static OnOffType getOnOff(int value) {

View File

@ -88,20 +88,24 @@ 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/Pro devices
# 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.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.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.shellypro3em.description = Shelly Pro 3EM - 3xPower Meter
thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch with Power Meter
@ -111,6 +115,7 @@ thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch wit
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.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
thing-type.shelly.shellypro1.description = Shelly Pro 1 - Single Relay Switch
@ -290,6 +295,8 @@ channel-type.shelly.lastPower1.label = Last Power
channel-type.shelly.lastPower1.description = Last power consumption #1 - one rounded minute
channel-type.shelly.meterTotal.label = Total Energy Consumption
channel-type.shelly.meterTotal.description = Total energy consumption in kW/h since the device powered up (resets on restart)
channel-type.shelly.meterResetTotals.label = Reset Totals
channel-type.shelly.meterResetTotals.description = Resets totals measurement data
channel-type.shelly.meterReturned.label = Total Returned Energy
channel-type.shelly.meterReturned.description = Total returned energy in kW/h
channel-type.shelly.meterVoltage.label = Voltage
@ -348,6 +355,8 @@ channel-type.shelly.sensorFlood.label = Flood Alarm
channel-type.shelly.sensorFlood.description = Indicates flood / water detection when toggled ON
channel-type.shelly.sensorSmoke.label = Smoke Alarm
channel-type.shelly.sensorSmoke.description = Indicates smoke detection when toggled ON
channel-type.shelly.sensorMute.label = Mute
channel-type.shelly.sensorMute.description = Indicates mute setting (ON=muted)
channel-type.shelly.sensorLux.label = Lux
channel-type.shelly.sensorLux.description = Brightness from the sensor (Lux)
channel-type.shelly.sensorIllumination.label = Illumination
@ -398,6 +407,10 @@ channel-type.shelly.sensorContact.label = State
channel-type.shelly.sensorContact.description = State of the contact (open/closed)
channel-type.shelly.sensorContact.state.option.OPEN = Open
channel-type.shelly.sensorContact.state.option.CLOSED = Closed
channel-type.shelly.sensorOpen.label = Open
channel-type.shelly.sensorOpen.description = OPEN or CLOSED
channel-type.shelly.sensorOpen.state.option.OPEN = Open
channel-type.shelly.sensorOpen.state.option.CLOSED = Closed
channel-type.shelly.sensorState.label = Sensor State
channel-type.shelly.sensorState.description = Sensor State (Warm-Up/Normal/Fault/Unknown)
channel-type.shelly.sensorState.state.option.warmup = Warm-Up

View File

@ -580,6 +580,12 @@
</state>
</channel-type>
<channel-type id="meterResetTotals">
<item-type>Switch</item-type>
<label>@text/channel-type.shelly.meterResetTotals.label</label>
<description>@text/channel-type.shelly.meterResetTotals.description</description>
</channel-type>
<channel-type id="timestamp">
<item-type>DateTime</item-type>
<label>@text/channel-type.shelly.timestamp.label</label>

View File

@ -243,6 +243,18 @@
</state>
</channel-type>
<channel-type id="sensorOpen">
<item-type>Contact</item-type>
<label>@text/channel-type.shelly.sensorOpen.label</label>
<description>@text/channel-type.shelly.sensorOpen.description</description>
<state pattern="%s" readOnly="true">
<options>
<option value="OPEN">@text/channel-type.shelly.sensorOpen.state.option.OPEN</option>
<option value="CLOSED">@text/channel-type.shelly.sensorOpen.state.option.CLOSED</option>
</options>
</state>
</channel-type>
<channel-type id="sensorState">
<item-type>String</item-type>
<label>@text/channel-type.shelly.sensorState.label</label>
@ -339,6 +351,12 @@
</state>
</channel-type>
<channel-type id="sensorMute">
<item-type>Switch</item-type>
<label>@text/channel-type.shelly.sensorMute.label</label>
<description>@text/channel-type.shelly.sensorMute.description</description>
</channel-type>
<channel-type id="sensorLux">
<item-type>Number:Illuminance</item-type>
<label>@text/channel-type.shelly.sensorLux.label</label>

View File

@ -60,6 +60,20 @@
<config-description-ref uri="thing-type:shelly:roller-gen2"/>
</thing-type>
<thing-type id="shellyplusplug">
<label>ShellyPlus Plug</label>
<description>@text/thing-type.shelly.shellyplusplug.description</description>
<category>PowerOutlet</category>
<channel-groups>
<channel-group id="relay" typeId="relayChannelPlug"/>
<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:relay-gen2"/>
</thing-type>
<thing-type id="shellyplusi4">
<label>ShellyPlus i4</label>
@ -219,6 +233,26 @@
<config-description-ref uri="thing-type:shelly:relay-gen2"/>
</thing-type>
<thing-type id="shellypro3em">
<label>Shelly Pro 3EM</label>
<description>@text/thing-type.shelly.shellypro3em.description</description>
<channel-groups>
<channel-group id="meter1" typeId="meter">
<label>@text/channel-group-type.shelly.meter1.label</label>
</channel-group>
<channel-group id="meter2" typeId="meter">
<label>@text/channel-group-type.shelly.meter2.label</label>
</channel-group>
<channel-group id="meter3" typeId="meter">
<label>@text/channel-group-type.shelly.meter3.label</label>
</channel-group>
<channel-group id="relay" typeId="relayChannel"/>
<channel-group id="device" typeId="deviceStatus"/>
</channel-groups>
<representation-property>serviceName</representation-property>
<config-description-ref uri="thing-type:shelly:relay"/>
</thing-type>
<thing-type id="shellypro4pm">
<label>ShellyPro 4PM</label>

View File

@ -18,4 +18,18 @@
<config-description-ref uri="thing-type:shelly:battery-gen2"/>
</thing-type>
<thing-type id="shellyplussmoke">
<label>Shelly Plus Smoke</label>
<description>@text/thing-type.shelly.shellyplussmoke.description</description>
<category>SmokeDetector</category>
<channel-groups>
<channel-group id="sensors" typeId="sensorData"/>
<channel-group id="battery" typeId="batteryStatus"/>
<channel-group id="device" typeId="deviceStatus"/>
</channel-groups>
<representation-property>serviceName</representation-property>
<config-description-ref uri="thing-type:shelly:battery-gen2"/>
</thing-type>
</thing:thing-descriptions>