| | input | Switch | yes | ON: Input/Button is powered, see General Notes on Channels |
| | 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 |
+| | lastPower1 | Number | yes | The average power for the previous minute |
| | totalKWH | Number | yes | Total energy consumption in kwh since the device powered up (resets on restart) |
| | | | | |
| | lastUpdate | DateTime | yes | Timestamp of the last measurement |
| | 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 |
+| nmeter | ncurrent | Number | yes | Current current based on N clamp (requires calibration) |
+| | ixsum | Number | yes | Measured current over all phases |
+| | nmismatch | Switch | yes | ON: abs(ncurrent-ixsum) is greater than nmTreshhold |
+| | nmTreshhold | Number | yes | Treshhod (delta) before nMismatch goes ON |
+
+_Note:
+You should calibrate the device if you want to use "neutral current" measurements.
+Check the Shelly documentation for details._
+
### Shelly 2 - relay mode (thing-type: shelly2-relay)
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_NMETER = "nmeter";
+ public static final String CHANNEL_NMETER_CURRENT = "ncurrent";
+ public static final String CHANNEL_NMETER_IXSUM = "ixsum";
+ public static final String CHANNEL_NMETER_MISMATCH = "nmismatch";
+ public static final String CHANNEL_NMETER_MTRESHHOLD = "nmTreshhold";
public static final String CHANNEL_GROUP_SENSOR = "sensors";
public static final String CHANNEL_SENSOR_TEMP = "temperature";
public Double current; // 3EM
}
+ public static class ShellyEMNCurrentSettings {
+ // "emeter_n":{ "range_extender":1, "mismatch_threshold":0.00}
+ @SerializedName("range_extender")
+ public Integer rangeExtender;
+ @SerializedName("mismatch_threshold")
+ public Double mismatchThreshold;
+ }
+
+ public static class ShellyEMNCurrentStatus {
+ // "emeter_n":{"current":2.28,"ixsum":2.29,"mismatch":false,"is_valid":true}
+ public Double current;
+ public Double ixsum;
+ public Boolean mismatch;
+ @SerializedName("is_valid")
+ public Boolean isValid;
+ }
+
public static class ShellySettingsUpdate {
public String status;
@SerializedName("has_update")
public @Nullable ArrayList<ShellySettingsRoller> rollers;
public @Nullable ArrayList<ShellySettingsRgbwLight> lights;
public @Nullable ArrayList<ShellySettingsEMeter> emeters;
+ @SerializedName("emeter_n")
+ public ShellyEMNCurrentSettings neutralCurrent;
public @Nullable ArrayList<ShellyThermnostat> thermostats; // TRV
@SerializedName("ext_switch_enable")
public ArrayList<ShellySettingsMeter> meters;
public ArrayList<ShellySettingsEMeter> emeters;
+ @SerializedName("emeter_n")
+ public ShellyEMNCurrentStatus neutralCurrent;
+
public Double totalCurrent;
public Double totalPower;
public Double totalReturned;
processed = true;
switch (sen.id) {
- case "6": // 3EM: neutralCurrent
- break;
-
case "3106": // L, luminosity, lux, U32, -1
case "3110": // S, luminosityLevel, dark/twilight/bright, "unknown"=unknown
case "3111": // B, battery, 0-100%, unknown -1
case "4209": // emeter_1: A, current, 0/120A, -1
case "4309": // emeter_2: A, current, 0/120A, -1
updateChannel(updates, rGroup, CHANNEL_EMETER_CURRENT,
- toQuantityType(getDouble(s.value), DIGITS_VOLT, Units.AMPERE));
+ toQuantityType(getDouble(s.value), DIGITS_AMPERE, Units.AMPERE));
break;
case "4110": // emeter_0: S, powerFactor, 0/1, -1
updateChannel(updates, rGroup, CHANNEL_EMETER_PFACTOR, getDecimal(s.value));
break;
+ case "6": // 3EM: emeter_n: nCurrent
+ updateChannel(updates, CHANNEL_GROUP_NMETER, CHANNEL_NMETER_CURRENT,
+ toQuantityType(value, DIGITS_AMPERE, Units.AMPERE));
+ break;
+
case "5101": // {"I":5101,"T":"S","D":"brightness","R":"0/100","L":1},
case "5102": // {"I":5102,"T":"S","D":"gain","R":"0/100","L":1},
case "5103": // {"I":5103,"T":"S","D":"colorTemp","U":"K","R":"3000/6500","L":1},
if (!valid) {
logger.debug("{}: WARNING: Incompatible device description detected for CoIoT version {}!", thingName,
coiot.getVersion());
+ return;
}
coiot.completeMissingSensorDefinition(sensorMap); // fix incomplete format
// This happens on firmware up/downgrades (version 1.8 brings CoIoT v2 with 4 digit IDs)
int vers = coiot.getVersion();
if (((vers == COIOT_VERSION_1) && (sen.id.length() > 3))
- || ((vers >= COIOT_VERSION_2) && (sen.id.length() < 4))) {
- logger.debug("{}: Invalid format for sensor defition detected, id={}", thingName, sen.id);
+ || ((vers >= COIOT_VERSION_2) && (sen.id.length() < 4) && !sen.id.equals("6"))) {
+ logger.debug("{}: Invalid format for sensor definition detected, id={}", thingName, sen.id);
return false;
}
m++;
}
} else {
+ if (status.neutralCurrent != null) {
+ if (!thingHandler.areChannelsCreated()) {
+ thingHandler.updateChannelDefinitions(ShellyChannelDefinitions.createEMNCurrentChannels(
+ thingHandler.getThing(), profile.settings.neutralCurrent, status.neutralCurrent));
+ }
+ if (getBool(status.neutralCurrent.isValid)) {
+ String ngroup = CHANNEL_GROUP_NMETER;
+ updated |= thingHandler.updateChannel(ngroup, CHANNEL_NMETER_CURRENT, toQuantityType(
+ getDouble(status.neutralCurrent.current), DIGITS_AMPERE, Units.AMPERE));
+ updated |= thingHandler.updateChannel(ngroup, CHANNEL_NMETER_IXSUM, toQuantityType(
+ getDouble(status.neutralCurrent.ixsum), DIGITS_AMPERE, Units.AMPERE));
+ updated |= thingHandler.updateChannel(ngroup, CHANNEL_NMETER_MTRESHHOLD,
+ toQuantityType(getDouble(profile.settings.neutralCurrent.mismatchThreshold),
+ DIGITS_AMPERE, Units.AMPERE));
+ updated |= thingHandler.updateChannel(ngroup, CHANNEL_NMETER_MISMATCH,
+ getOnOff(status.neutralCurrent.mismatch));
+ }
+ }
+
for (ShellySettingsEMeter emeter : status.emeters) {
if (getBool(emeter.isValid)) {
String groupName = profile.getMeterGroup(m);
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
+import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyEMNCurrentSettings;
+import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyEMNCurrentStatus;
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.ShellySettingsDimmer;
private static final String CHGR_LIGHTCH = CHANNEL_GROUP_LIGHT_CHANNEL;
private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
private static final String CHGR_METER = CHANNEL_GROUP_METER;
+ private static final String CHGR_EMN = CHANNEL_GROUP_NMETER;
private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
private static final String CHGR_CONTROL = CHANNEL_GROUP_CONTROL;
private static final String CHGR_BAT = CHANNEL_GROUP_BATTERY;
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEMT_NUMBER))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_RESETTOTAL, "meterResetTotals", ITEMT_SWITCH))
+ // 3EM: neutral current (emeter_n)
+ .add(new ShellyChannel(m, CHGR_EMN, CHANNEL_NMETER_CURRENT, "ncurrent", ITEMT_AMP))
+ .add(new ShellyChannel(m, CHGR_EMN, CHANNEL_NMETER_IXSUM, "ixsum", ITEMT_AMP))
+ .add(new ShellyChannel(m, CHGR_EMN, CHANNEL_NMETER_MTRESHHOLD, "nmTreshhold", ITEMT_AMP))
+ .add(new ShellyChannel(m, CHGR_EMN, CHANNEL_NMETER_MISMATCH, "nmismatch", ITEMT_SWITCH))
+
// Sensors
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEMT_TEMP))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_HUM, "sensorHumidity", ITEMT_PERCENT))
String group = substringBefore(channelName, "#");
String channel = substringAfter(channelName, "#");
- if (group.contains(CHANNEL_GROUP_METER)) {
+ if (group.startsWith(CHANNEL_GROUP_METER)) {
group = CHANNEL_GROUP_METER; // map meter1..n to meter
- } else if (group.contains(CHANNEL_GROUP_RELAY_CONTROL)) {
+ } else if (group.startsWith(CHANNEL_GROUP_RELAY_CONTROL)) {
group = CHANNEL_GROUP_RELAY_CONTROL; // map meter1..n to meter
- } else if (group.contains(CHANNEL_GROUP_LIGHT_CHANNEL)) {
+ } else if (group.startsWith(CHANNEL_GROUP_LIGHT_CHANNEL)) {
group = CHANNEL_GROUP_LIGHT_CHANNEL;
- } else if (group.contains(CHANNEL_GROUP_STATUS)) {
+ } else if (group.startsWith(CHANNEL_GROUP_STATUS)) {
group = CHANNEL_GROUP_STATUS; // map status1..n to meter
}
return newChannels;
}
+ public static Map<String, Channel> createEMNCurrentChannels(final Thing thing, ShellyEMNCurrentSettings settings,
+ ShellyEMNCurrentStatus status) {
+ String group = CHANNEL_GROUP_NMETER;
+ Map<String, Channel> newChannels = new LinkedHashMap<>();
+ addChannel(thing, newChannels, status.current != null, group, CHANNEL_NMETER_CURRENT);
+ addChannel(thing, newChannels, status.ixsum != null, group, CHANNEL_NMETER_IXSUM);
+ addChannel(thing, newChannels, status.mismatch != null, group, CHANNEL_NMETER_MISMATCH);
+ addChannel(thing, newChannels, settings.mismatchThreshold != null, group, CHANNEL_NMETER_MTRESHHOLD);
+ return newChannels;
+ }
+
public static Map<String, Channel> createSensorChannels(final Thing thing, final ShellyDeviceProfile profile,
final ShellyStatusSensor sdata) {
Map<String, Channel> newChannels = new LinkedHashMap<>();
channel-group-type.shelly.meter3.label = Power Meter 3
channel-group-type.shelly.meter4.label = Power Meter 4
channel-group-type.shelly.meter.description = Power consumption for the relay
+channel-group-type.shelly.nmeter.label = Neutral Current
+channel-group-type.shelly.nmeter.description = Based measurements based on the N clamp (has to be calibrated)
channel-group-type.shelly.externalSensors.label = External Sensors
channel-group-type.shelly.externalSensors.description = Temperatures from external sensors connected to the optional Addon
channel-type.shelly.meterCurrent.description = Current in A
channel-type.shelly.meterPowerFactor.label = Power Factor
channel-type.shelly.meterPowerFactor.description = Power Factor in percent for photovoltaic
+channel-type.shelly.ncurrent.label = Neutral Current
+channel-type.shelly.ncurrent.description = Measured neutral current
+channel-type.shelly.ixsum.label = Accumulated Current
+channel-type.shelly.ixsum.description = Sum of measured current on all phases
+channel-type.shelly.nmismatch.label = N-Current Mismatch
+channel-type.shelly.nmismatch.description = ON: Sum of all signed currents of the 4 wires is greater than the configured N Current Treshhold
+channel-type.shelly.nmTreshhold.label = Mismatch Treshhold
+channel-type.shelly.nmTreshhold.description = Mismatch becomes ON when treshhold is exceeded
channel-type.shelly.timestamp.label = Last Update
channel-type.shelly.timestamp.description = Timestamp of last measurement
channel-type.shelly.ledStatusDisable.label = Disable Status LED
<channel-group id="meter3" typeId="meter">
<label>@text/channel-group-type.shelly.meter3.label</label>
</channel-group>
+ <channel-group id="nmeter" typeId="nmeter"/>
<channel-group id="relay" typeId="relayChannel"/>
<channel-group id="device" typeId="deviceStatus"/>
</channel-groups>
<description>@text/channel-group-type.shelly.meter.description</description>
</channel-group-type>
+ <channel-group-type id="nmeter">
+ <label>@text/channel-group-type.shelly.nmeter.label</label>
+ <description>@text/channel-group-type.shelly.nmeter.description</description>
+ </channel-group-type>
+
<channel-group-type id="externalSensors">
<label>@text/channel-group-type.shelly.externalSensors.label</label>
<description>@text/channel-group-type.shelly.externalSensors.description</description>
<description>@text/channel-type.shelly.meterResetTotals.description</description>
</channel-type>
+ <channel-type id="ncurrent">
+ <item-type>Number:ElectricCurrent</item-type>
+ <label>@text/channel-type.shelly.ncurrent.label</label>
+ <description>@text/channel-type.shelly.ncurrent.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="ixsum">
+ <item-type>Number:ElectricCurrent</item-type>
+ <label>@text/channel-type.shelly.ixsum.label</label>
+ <description>@text/channel-type.shelly.ixsum.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="nmTreshhold">
+ <item-type>Number:ElectricCurrent</item-type>
+ <label>@text/channel-type.shelly.nmTreshhold.label</label>
+ <description>@text/channel-type.shelly.nmTreshhold.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="nmismatch">
+ <item-type>Switch</item-type>
+ <label>@text/channel-type.shelly.nmismatch.label</label>
+ <description>@text/channel-type.shelly.nmismatch.description</description>
+ </channel-type>
+
<channel-type id="timestamp">
<item-type>DateTime</item-type>
<label>@text/channel-type.shelly.timestamp.label</label>