Skip to content

matter_powermeter.tc

Matter 1.4 Power + Energy Meter — expose an SML meter over Matter.

Source on GitHub

// Matter 1.4 Power + Energy Meter — expose an SML meter over Matter.
//
// Matter 1.4 added the Electrical Sensor device type (0x0510) with the
// Electrical Power Measurement (0x0090) and Electrical Energy Measurement
// (0x0091) clusters. This publishes the live values a Tasmota SML meter reads
// so a Matter controller (e.g. Home Assistant) can show power AND energy.
//
//   Endpoint 1: Electrical Sensor (0x0510)
//     -> ElectricalPowerMeasurement (0x0090):
//          ActivePower   (0x0008) int64 mW   — may be negative (grid export)
//          Voltage       (0x0004) int64 mV
//          ActiveCurrent (0x0005) int64 mA
//     -> ElectricalEnergyMeasurement (0x0091):
//          CumulativeEnergyImported (0x0001) EnergyMeasurementStruct {Energy mWh}
//          CumulativeEnergyExported (0x0002) EnergyMeasurementStruct {Energy mWh}
//
// Values are int64; publish with matterSetFloat(ep, cluster, attr, value, scale)
// which stores round(value*scale). Power uses scale 1000 (W->mW); cumulative
// energy uses scale 1000000 (kWh->mWh). Declare those attributes as MTR_S64; the
// engine wraps the energy value in the required EnergyMeasurementStruct.
//
// NOTE: simulated so it runs on any board. For a real meter, read SML values:
//     w   = sensorGet("SML#Power_curr");     // Watts
//     imp = sensorGet("SML#Total_in");       // kWh imported (cumulative)
//     exp = sensorGet("SML#Total_out");      // kWh exported (cumulative)
//
// Limitation: the optional EnergyMeasurementStruct timestamps and the EPM
// Accuracy/Ranges struct attributes are not emitted (not required for readout).

int ep;
int tick;
float kwh_imp;     // cumulative imported energy (kWh)
float kwh_exp;     // cumulative exported energy (kWh)

void EverySecond() {
    float w; float v; float a;
    tick = tick + 1;
    // Simulated readings (swap for sensorGet(...) on a real SML meter).
    w = 800.0 - 1500.0 * sin((float)tick / 30.0);   // -700 .. +2300 W (export when negative)
    v = 230.0 + 3.0  * sin((float)tick / 17.0);     // ~230 V
    a = w / v;                                       // rough current (A)
    matterSetFloat(ep, CLUSTER_POWER, ATTR_ACTIVE_POWER,   w, 1000);  // W -> mW
    matterSetFloat(ep, CLUSTER_POWER, ATTR_VOLTAGE,        v, 1000);  // V -> mV
    matterSetFloat(ep, CLUSTER_POWER, ATTR_ACTIVE_CURRENT, a, 1000);  // A -> mA

    // Integrate power into energy: W for 1 s = W/3600000 kWh.
    if (w > 0.0) { kwh_imp = kwh_imp + w / 3600000.0; }
    if (w < 0.0) { kwh_exp = kwh_exp - w / 3600000.0; }
    matterSetFloat(ep, CLUSTER_ENERGY, ATTR_ENERGY_IMPORT, kwh_imp, 1000000);  // kWh -> mWh
    matterSetFloat(ep, CLUSTER_ENERGY, ATTR_ENERGY_EXPORT, kwh_exp, 1000000);
}

int main() {
    tick = 0;
    kwh_imp = 1234.5;                  // seed with a realistic meter reading
    kwh_exp = 56.7;
    matterReset();

    ep = matterAdd(MATTER_ELEC_SENSOR);
    matterName(ep, "Power Meter");                                // accessory title in the controller

    matterCluster(ep, CLUSTER_POWER);                            // ElectricalPowerMeasurement
    matterAttr(ep, CLUSTER_POWER, ATTR_ACTIVE_POWER,   MTR_S64); // mW
    matterAttr(ep, CLUSTER_POWER, ATTR_VOLTAGE,        MTR_S64); // mV
    matterAttr(ep, CLUSTER_POWER, ATTR_ACTIVE_CURRENT, MTR_S64); // mA
    matterSetFloat(ep, CLUSTER_POWER, ATTR_ACTIVE_POWER,   0.0, 1000);
    matterSetFloat(ep, CLUSTER_POWER, ATTR_VOLTAGE,      230.0, 1000);
    matterSetFloat(ep, CLUSTER_POWER, ATTR_ACTIVE_CURRENT, 0.0, 1000);

    matterCluster(ep, CLUSTER_ENERGY);                            // ElectricalEnergyMeasurement
    matterAttr(ep, CLUSTER_ENERGY, ATTR_ENERGY_IMPORT, MTR_S64);  // mWh (wrapped in a struct)
    matterAttr(ep, CLUSTER_ENERGY, ATTR_ENERGY_EXPORT, MTR_S64);
    matterSetFloat(ep, CLUSTER_ENERGY, ATTR_ENERGY_IMPORT, kwh_imp, 1000000);
    matterSetFloat(ep, CLUSTER_ENERGY, ATTR_ENERGY_EXPORT, kwh_exp, 1000000);

    matterStart();                     // Matter on; press Bind on /mt to pair
    return 0;
}