]> git.basschouten.com Git - openhab-addons.git/commitdiff
[shelly] New channel group ncurrent for 3EM (#16336)
authorMarkus Michels <markus7017@gmail.com>
Sat, 17 Feb 2024 14:36:20 +0000 (15:36 +0100)
committerGitHub <noreply@github.com>
Sat, 17 Feb 2024 14:36:20 +0000 (15:36 +0100)
* Add channels for emeter_n (neutral current-based measurements) - polled
status and CoAP update (ncurrent only, no other values)

Signed-off-by: Markus Michels <markus7017@gmail.com>
bundles/org.openhab.binding.shelly/README.md
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1ApiJsonDTO.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoIoTVersion2.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoapHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyComponents.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyChannelDefinitions.java
bundles/org.openhab.binding.shelly/src/main/resources/OH-INF/i18n/shelly.properties
bundles/org.openhab.binding.shelly/src/main/resources/OH-INF/thing/shellyGen1_relay.xml

index 89b34d8e41612d2f649d9efe407a78b06f699fbb..389e8ed8188a479a75d69e24b61d22d0384a3b43 100644 (file)
@@ -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                                               |
@@ -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)
 
index ba0fd6b0eea9f0b2996daf4097b79d85178d5916..1dcf6dd99cad510290909317a78eb193782c59c7 100755 (executable)
@@ -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";
index ec99dda4b7e486ae11b4822ee810a75a2a165b3d..ffb2e3566772c191644268ba8944a48d5c9c277a 100644 (file)
@@ -553,6 +553,23 @@ public class Shelly1ApiJsonDTO {
         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")
@@ -621,6 +638,8 @@ public class Shelly1ApiJsonDTO {
         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")
@@ -745,6 +764,9 @@ public class Shelly1ApiJsonDTO {
         public ArrayList<ShellySettingsMeter> meters;
 
         public ArrayList<ShellySettingsEMeter> emeters;
+        @SerializedName("emeter_n")
+        public ShellyEMNCurrentStatus neutralCurrent;
+
         public Double totalCurrent;
         public Double totalPower;
         public Double totalReturned;
index fc295303695998128db1a1d0b4dbb4ad223f05d6..8dce4010b3a9777e779867d5f372ed0e0b03363a 100644 (file)
@@ -142,9 +142,6 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
 
         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
@@ -307,7 +304,7 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
             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
@@ -316,6 +313,11 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
                 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},
index 9ab5e29c39bb794bca20600161ca66a0f8ad573c..d3fe61dc63f5d9841e49e5abbac5ace46db7f4fd 100644 (file)
@@ -362,6 +362,7 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
             if (!valid) {
                 logger.debug("{}: WARNING: Incompatible device description detected for CoIoT version {}!", thingName,
                         coiot.getVersion());
+                return;
             }
 
             coiot.completeMissingSensorDefinition(sensorMap); // fix incomplete format
@@ -386,8 +387,8 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
         // 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;
         }
 
index fe6e70a5cd92f4e707e065e0dcfd0f3e11f1149a..1d5a0cc133234c59105971ed33a68b8e4f44df0b 100644 (file)
@@ -264,6 +264,25 @@ public class ShellyComponents {
                         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);
index 3c7d1af24bd72cecc00a5981809f0ad68e1ff065..5ad1b4568206725ae8300c38a89cfda55a3e18c6 100644 (file)
@@ -31,6 +31,8 @@ import javax.measure.Unit;
 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;
@@ -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;
@@ -203,6 +206,12 @@ public class ShellyChannelDefinitions {
                 .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))
@@ -264,13 +273,13 @@ public class ShellyChannelDefinitions {
         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
         }
 
@@ -491,6 +500,17 @@ public class ShellyChannelDefinitions {
         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<>();
index 987c3e5089f3ee31c26c445ec6d8d4f3a49fa5b8..2e90f7861884590d3abcae4179428aed836ce626 100644 (file)
@@ -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
 
@@ -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
index 0a252956acb5bccc75efabbc02ef769bcab0dac2..5e0fb7371126c4190fc12ff9aba120eba02aea85 100644 (file)
@@ -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>
                <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>