Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[shelly] New channel group ncurrent for 3EM #16336

Merged
merged 8 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion bundles/org.openhab.binding.shelly/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ In this case the is no real measurement based on power consumption, but the Shel
| | 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 |
Expand Down Expand Up @@ -573,6 +573,15 @@ The Thing id is derived from the service name, so that's the reason why the Thin
| | 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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public class ShellyBindingConstants {
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,23 @@ public static class ShellySettingsEMeter { // ShellyEM meter
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")
Expand Down Expand Up @@ -621,6 +638,8 @@ public static class ShellySettingsGlobal {
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")
Expand Down Expand Up @@ -745,6 +764,9 @@ public static class ShellySettingsStatus {
public ArrayList<ShellySettingsMeter> meters;

public ArrayList<ShellySettingsEMeter> emeters;
@SerializedName("emeter_n")
public ShellyEMNCurrentStatus neutralCurrent;

public Double totalCurrent;
public Double totalPower;
public Double totalReturned;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,6 @@ public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen

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
Expand Down Expand Up @@ -307,7 +304,7 @@ public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen
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
Expand All @@ -316,6 +313,11 @@ public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen
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},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ private void handleDeviceDescription(String devId, String payload) throws Shelly
if (!valid) {
logger.debug("{}: WARNING: Incompatible device description detected for CoIoT version {}!", thingName,
coiot.getVersion());
return;
}

coiot.completeMissingSensorDefinition(sensorMap); // fix incomplete format
Expand All @@ -386,8 +387,8 @@ private synchronized boolean addSensor(CoIotDescrSen sen) {
// 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,25 @@ public static boolean updateMeters(ShellyThingInterface thingHandler, ShellySett
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
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;
Expand Down Expand Up @@ -94,6 +96,7 @@ public class ShellyChannelDefinitions {
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;
Expand Down Expand Up @@ -203,6 +206,12 @@ public ShellyChannelDefinitions(@Reference ShellyTranslationProvider translation
.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))
Expand Down Expand Up @@ -264,13 +273,13 @@ public ShellyChannelDefinitions(@Reference ShellyTranslationProvider translation
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
}

Expand Down Expand Up @@ -491,6 +500,17 @@ public static Map<String, Channel> createEMeterChannels(final Thing thing, final
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<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ channel-group-type.shelly.meter2.label = Power Meter 2
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

Expand Down Expand Up @@ -310,6 +312,14 @@ channel-type.shelly.meterCurrent.label = Current
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
<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>
Expand Down Expand Up @@ -328,6 +329,11 @@
<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>
Expand Down Expand Up @@ -604,6 +610,51 @@
<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>
Expand Down