Enhance features for EMIZB-151 device#10458
Conversation
This is related to new converter file for the existing Frient EMIZB-151 device. Please see the issue bellow with new converter and details about the update: Koenkk#10457
|
I hope I did this correctly, I don't really want to change the description of the device but change the converter linked to it, please check #10457 |
|
Could you update this PR with the ext converter? |
|
Here is the converter, as explained in #10457, this is an update of an existing converter, here are the updates:
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const ota = require('zigbee-herdsman-converters/lib/ota');
const e = exposes.presets;
const ea = exposes.access;
const definition = {
zigbeeModel: ['EMIZB-151'],
model: 'EMIZB-151',
vendor: 'Frient',
description: 'Electricity Meter Interface 2 P1',
fromZigbee: [
{
cluster: 'haElectricalMeasurement',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
const result = {};
// Power - Apply multiplier/divisor scaling
const getPowerFactor = () => {
const multiplier = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acPowerMultiplier');
const divisor = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acPowerDivisor');
return multiplier && divisor ? multiplier / divisor : 1;
};
const powerFactor = getPowerFactor();
if ('activePower' in msg.data) {
let power = msg.data.activePower * powerFactor;
// Apply power calibration if set
const calibration = meta.device.meta?.power_calibration || 0;
if (calibration !== 0) {
power = power * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.power_precision ?? 1;
power = Number(power.toFixed(precision));
result.power = power;
result.power_phase_a = power;
}
if ('activePowerPhB' in msg.data) {
let power = msg.data.activePowerPhB * powerFactor;
// Apply power phase B calibration if set
const calibration = meta.device.meta?.power_phase_b_calibration || 0;
if (calibration !== 0) {
power = power * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.power_phase_b_precision ?? 1;
power = Number(power.toFixed(precision));
result.power_phase_b = power;
}
if ('activePowerPhC' in msg.data) {
let power = msg.data.activePowerPhC * powerFactor;
// Apply power phase C calibration if set
const calibration = meta.device.meta?.power_phase_c_calibration || 0;
if (calibration !== 0) {
power = power * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.power_phase_c_precision ?? 1;
power = Number(power.toFixed(precision));
result.power_phase_c = power;
}
// Calculate power_total using current values and previous state
// This ensures we use values from previous messages if not in current message
const phaseA = result.power_phase_a ?? meta.state.power_phase_a ?? 0;
const phaseB = result.power_phase_b ?? meta.state.power_phase_b ?? 0;
const phaseC = result.power_phase_c ?? meta.state.power_phase_c ?? 0;
result.power_total = phaseA + phaseB + phaseC;
// Voltage - Apply multiplier/divisor scaling
const getVoltageFactor = () => {
const multiplier = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acVoltageMultiplier');
const divisor = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acVoltageDivisor');
return multiplier && divisor ? multiplier / divisor : 0.1; // Default to /10 if not available
};
const voltageFactor = getVoltageFactor();
if ('rmsVoltage' in msg.data) {
let voltage = msg.data.rmsVoltage * voltageFactor;
// Apply voltage calibration if set
const calibration = meta.device.meta?.voltage_calibration || 0;
if (calibration !== 0) {
voltage = voltage * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.voltage_precision ?? 1;
voltage = Number(voltage.toFixed(precision));
result.voltage = voltage;
result.voltage_phase_a = voltage;
}
if ('rmsVoltagePhB' in msg.data) {
let voltage = msg.data.rmsVoltagePhB * voltageFactor;
// Apply voltage phase B calibration if set
const calibration = meta.device.meta?.voltage_phase_b_calibration || 0;
if (calibration !== 0) {
voltage = voltage * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.voltage_phase_b_precision ?? 1;
voltage = Number(voltage.toFixed(precision));
result.voltage_phase_b = voltage;
}
if ('rmsVoltagePhC' in msg.data) {
let voltage = msg.data.rmsVoltagePhC * voltageFactor;
// Apply voltage phase C calibration if set
const calibration = meta.device.meta?.voltage_phase_c_calibration || 0;
if (calibration !== 0) {
voltage = voltage * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.voltage_phase_c_precision ?? 1;
voltage = Number(voltage.toFixed(precision));
result.voltage_phase_c = voltage;
}
// Current - Apply multiplier/divisor scaling
const getCurrentFactor = () => {
const multiplier = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acCurrentMultiplier');
const divisor = msg.endpoint.getClusterAttributeValue('haElectricalMeasurement', 'acCurrentDivisor');
return multiplier && divisor ? multiplier / divisor : 0.01; // Default to /100 if not available
};
const currentFactor = getCurrentFactor();
if ('rmsCurrent' in msg.data) {
let current = msg.data.rmsCurrent * currentFactor;
// Apply current calibration if set
const calibration = meta.device.meta?.current_calibration || 0;
if (calibration !== 0) {
current = current * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.current_precision ?? 2;
current = Number(current.toFixed(precision));
result.current = current;
result.current_phase_a = current;
}
if ('rmsCurrentPhB' in msg.data) {
let current = msg.data.rmsCurrentPhB * currentFactor;
// Apply current phase B calibration if set
const calibration = meta.device.meta?.current_phase_b_calibration || 0;
if (calibration !== 0) {
current = current * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.current_phase_b_precision ?? 2;
current = Number(current.toFixed(precision));
result.current_phase_b = current;
}
if ('rmsCurrentPhC' in msg.data) {
let current = msg.data.rmsCurrentPhC * currentFactor;
// Apply current phase C calibration if set
const calibration = meta.device.meta?.current_phase_c_calibration || 0;
if (calibration !== 0) {
current = current * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.current_phase_c_precision ?? 2;
current = Number(current.toFixed(precision));
result.current_phase_c = current;
}
if ('rmsCurrentN' in msg.data) {
let current = msg.data.rmsCurrentN * currentFactor;
// Apply current neutral calibration if set
const calibration = meta.device.meta?.current_neutral_calibration || 0;
if (calibration !== 0) {
current = current * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.current_neutral_precision ?? 2;
current = Number(current.toFixed(precision));
result.current_neutral = current;
}
return result;
},
},
{
cluster: 'seMetering',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
const result = {};
if ('currentSummDelivered' in msg.data) {
let energy = msg.data['currentSummDelivered'] / 1000;
// Apply energy calibration if set
const calibration = meta.device.meta?.energy_calibration || 0;
if (calibration !== 0) {
energy = energy * (1 + calibration / 100);
}
// Apply precision rounding
const precision = meta.device.meta?.energy_precision ?? 0;
const rounded = Number(energy.toFixed(precision));
result.energy = rounded;
result.energy_consumed = rounded;
}
if ('currentSummReceived' in msg.data) {
result.energy_produced = Math.round(msg.data['currentSummReceived'] / 1000);
}
if ('currentTier1SummDelivered' in msg.data) {
result.energy_consumed_tariff1 = Math.round(msg.data['currentTier1SummDelivered'] / 1000);
}
if ('currentTier2SummDelivered' in msg.data) {
result.energy_consumed_tariff2 = Math.round(msg.data['currentTier2SummDelivered'] / 1000);
}
if ('currentTier1SummReceived' in msg.data) {
result.energy_produced_tariff1 = Math.round(msg.data['currentTier1SummReceived'] / 1000);
}
if ('currentTier2SummReceived' in msg.data) {
result.energy_produced_tariff2 = Math.round(msg.data['currentTier2SummReceived'] / 1000);
}
return result;
},
}
],
toZigbee: [
{
key: [
'power_calibration', 'power_precision',
'voltage_calibration', 'voltage_precision',
'current_calibration', 'current_precision',
'energy_calibration', 'energy_precision',
'power_phase_b_calibration', 'power_phase_b_precision',
'power_phase_c_calibration', 'power_phase_c_precision',
'voltage_phase_b_calibration', 'voltage_phase_b_precision',
'voltage_phase_c_calibration', 'voltage_phase_c_precision',
'current_phase_b_calibration', 'current_phase_b_precision',
'current_phase_c_calibration', 'current_phase_c_precision',
'current_neutral_calibration', 'current_neutral_precision'
],
convertSet: async (entity, key, value, meta) => {
// Validate calibration inputs (-50 to +50%)
if (key.includes('_calibration')) {
if (typeof value !== 'number' || value < -50 || value > 50) {
throw new Error(`${key} must be a number between -50 and +50 (percentage)`);
}
}
// Validate precision inputs (0 to 5 decimal places)
if (key.includes('_precision')) {
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0 || value > 5) {
throw new Error(`${key} must be an integer between 0 and 5 (decimal places)`);
}
}
// Store the value in device metadata
meta.device.meta = meta.device.meta || {};
meta.device.meta[key] = value;
meta.device.save();
return {state: {[key]: value}};
},
convertGet: async (entity, key, meta) => {
// Return current value or default
let defaultValue = 0;
// Set defaults for precision based on measurement type
if (key.includes('_precision')) {
if (key.includes('power')) defaultValue = 1; // 1 decimal for power (W)
else if (key.includes('voltage')) defaultValue = 1; // 1 decimal for voltage (V)
else if (key.includes('current')) defaultValue = 2; // 2 decimals for current (A)
else if (key.includes('energy')) defaultValue = 0; // 0 decimals for energy (kWh)
}
const value = meta.device.meta?.[key] ?? defaultValue;
return {state: {[key]: value}};
},
},
],
exposes: [
e.power(),
e.voltage(),
e.current(),
e.numeric('power_phase_a', ea.STATE).withUnit('W').withDescription('Instantaneous measured power on Phase A'),
e.power_phase_b(),
e.power_phase_c(),
e.numeric('power_total', ea.STATE).withUnit('W').withDescription('Total instantaneous measured power (sum of all phases)'),
e.numeric('voltage_phase_a', ea.STATE).withUnit('V').withDescription('Measured electrical potential on Phase A'),
e.voltage_phase_b(),
e.voltage_phase_c(),
e.numeric('current_phase_a', ea.STATE).withUnit('A').withDescription('Instantaneous measured current on Phase A'),
e.current_phase_b(),
e.current_phase_c(),
e.numeric('current_neutral', ea.STATE).withUnit('A').withDescription('Instantaneous measured current on Neutral'),
e.energy(),
e.numeric('energy_consumed', ea.STATE).withUnit('kWh').withDescription('Total energy consumed from the grid - OBIS 1.8.0'),
e.numeric('energy_produced', ea.STATE).withUnit('kWh').withDescription('Total energy returned to the grid - OBIS 2.8.0'),
e.numeric('energy_consumed_tariff1', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 1 (peak/high) - OBIS 1.8.1'),
e.numeric('energy_consumed_tariff2', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 2 (off-peak/low) - OBIS 1.8.2'),
e.numeric('energy_produced_tariff1', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 1 (peak/high) - OBIS 2.8.1'),
e.numeric('energy_produced_tariff2', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 2 (off-peak/low) - OBIS 2.8.2'),
// Calibration and precision settings
e.numeric('power_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Power calibration adjustment in percentage (-50% to +50%)'),
e.numeric('power_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for power readings (0-5)'),
e.numeric('voltage_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Voltage calibration adjustment in percentage (-50% to +50%)'),
e.numeric('voltage_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for voltage readings (0-5)'),
e.numeric('current_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Current calibration adjustment in percentage (-50% to +50%)'),
e.numeric('current_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for current readings (0-5)'),
e.numeric('energy_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Energy calibration adjustment in percentage (-50% to +50%)'),
e.numeric('energy_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for energy readings (0-5)'),
// Phase B calibration and precision
e.numeric('power_phase_b_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Power Phase B calibration adjustment in percentage (-50% to +50%)'),
e.numeric('power_phase_b_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Power Phase B readings (0-5)'),
e.numeric('power_phase_c_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Power Phase C calibration adjustment in percentage (-50% to +50%)'),
e.numeric('power_phase_c_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Power Phase C readings (0-5)'),
e.numeric('voltage_phase_b_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Voltage Phase B calibration adjustment in percentage (-50% to +50%)'),
e.numeric('voltage_phase_b_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Voltage Phase B readings (0-5)'),
e.numeric('voltage_phase_c_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Voltage Phase C calibration adjustment in percentage (-50% to +50%)'),
e.numeric('voltage_phase_c_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Voltage Phase C readings (0-5)'),
e.numeric('current_phase_b_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Current Phase B calibration adjustment in percentage (-50% to +50%)'),
e.numeric('current_phase_b_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Current Phase B readings (0-5)'),
e.numeric('current_phase_c_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Current Phase C calibration adjustment in percentage (-50% to +50%)'),
e.numeric('current_phase_c_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Current Phase C readings (0-5)'),
e.numeric('current_neutral_calibration', ea.ALL).withUnit('%').withValueMin(-50).withValueMax(50).withValueStep(0.1)
.withDescription('Current Neutral calibration adjustment in percentage (-50% to +50%)'),
e.numeric('current_neutral_precision', ea.ALL).withValueMin(0).withValueMax(5).withValueStep(1)
.withDescription('Number of decimal places for Current Neutral readings (0-5)'),
],
ota: ota.zigbeeOTA,
endpoint: (device) => ({default: 2}),
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(2);
// Bind clusters - this is usually important
try {
await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement', 'seMetering']);
} catch (error) {
// Binding can fail but device may still work
}
// Configure electrical measurement reporting
await reporting.readEletricalMeasurementMultiplierDivisors(endpoint).catch(() => {});
// Configure all electrical measurement attributes with reasonable intervals
const electricalConfig = [
{ attr: 'activePower', change: 25 }, // Phase A power - report when changes by 25W
{ attr: 'activePowerPhB', change: 25 }, // Phase B power
{ attr: 'activePowerPhC', change: 25 }, // Phase C power
{ attr: 'rmsVoltage', change: 10 }, // Phase A voltage - report when changes by 1V (scaled by 10)
{ attr: 'rmsVoltagePhB', change: 10 }, // Phase B voltage
{ attr: 'rmsVoltagePhC', change: 10 }, // Phase C voltage
{ attr: 'rmsCurrent', change: 50 }, // Phase A current - report when changes by 0.5A (scaled by 100)
{ attr: 'rmsCurrentPhB', change: 50 }, // Phase B current
{ attr: 'rmsCurrentPhC', change: 50 }, // Phase C current
{ attr: 'rmsCurrentN', change: 50 } // Neutral current - report when changes by 0.5A (scaled by 100)
];
for (const config of electricalConfig) {
await endpoint.configureReporting('haElectricalMeasurement', [{
attribute: config.attr,
minimumReportInterval: 10, // Don't report more often than every 10 seconds
maximumReportInterval: 300, // Report at least every 5 minutes even if no change
reportableChange: config.change // Only report when value changes significantly
}]).catch(() => {});
}
// Read metering multiplier/divisor
await reporting.readMeteringMultiplierDivisor(endpoint).catch(() => {});
// Configure metering attributes
const meteringAttributes = [
'currentSummDelivered',
'currentSummReceived',
'currentTier1SummDelivered',
'currentTier2SummDelivered',
'currentTier1SummReceived',
'currentTier2SummReceived'
];
await endpoint.read('seMetering', meteringAttributes).catch(() => {});
for (const attr of meteringAttributes) {
await endpoint.configureReporting('seMetering', [{
attribute: attr,
minimumReportInterval: 300, // Don't report more often than every 5 minutes
maximumReportInterval: 3600, // Report at least every hour
reportableChange: 1000 // Report when energy changes by 1 kWh
}]).catch(() => {});
}
},
};
module.exports = definition; |
|
It's quite a lot of code, can we merge it with |
|
I shrinked it down to this by using m.electricityMeter, I will check if it can be merged with fz.electrical_measurement const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const m = require('zigbee-herdsman-converters/lib/modernExtend');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const e = exposes.presets;
const ea = exposes.access;
const definition = {
zigbeeModel: ['EMIZB-151'],
model: 'EMIZB-151',
vendor: 'Frient',
description: 'Electricity Meter Interface 2 P1',
// Use the official modern extend for three-phase electricity meter (includes all calibration and precision)
extend: [m.electricityMeter({threePhase: true})],
// Add additional seMetering functionality for energy tariffs
fromZigbee: [
{
cluster: 'seMetering',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
const result = {};
// Additional energy measurements for tariffs (not covered by standard electricityMeter)
if ('currentSummReceived' in msg.data) {
result.energy_produced = Math.round(msg.data['currentSummReceived'] / 1000);
}
if ('currentTier1SummDelivered' in msg.data) {
result.energy_consumed_tariff1 = Math.round(msg.data['currentTier1SummDelivered'] / 1000);
}
if ('currentTier2SummDelivered' in msg.data) {
result.energy_consumed_tariff2 = Math.round(msg.data['currentTier2SummDelivered'] / 1000);
}
if ('currentTier1SummReceived' in msg.data) {
result.energy_produced_tariff1 = Math.round(msg.data['currentTier1SummReceived'] / 1000);
}
if ('currentTier2SummReceived' in msg.data) {
result.energy_produced_tariff2 = Math.round(msg.data['currentTier2SummReceived'] / 1000);
}
return result;
},
}
],
// Additional exposes for the extra seMetering attributes not covered by electricityMeter
exposes: [
e.numeric('energy_produced', ea.STATE).withUnit('kWh').withDescription('Total energy returned to the grid - OBIS 2.8.0'),
e.numeric('energy_consumed_tariff1', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 1 (peak/high) - OBIS 1.8.1'),
e.numeric('energy_consumed_tariff2', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 2 (off-peak/low) - OBIS 1.8.2'),
e.numeric('energy_produced_tariff1', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 1 (peak/high) - OBIS 2.8.1'),
e.numeric('energy_produced_tariff2', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 2 (off-peak/low) - OBIS 2.8.2'),
],
ota: true,
endpoint: (device) => ({default: 2}),
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(2);
// Bind seMetering cluster for the additional energy tariff measurements
try {
await reporting.bind(endpoint, coordinatorEndpoint, ['seMetering']);
} catch (error) {
// Binding can fail but device may still work
}
// Read metering multiplier/divisor
await reporting.readMeteringMultiplierDivisor(endpoint).catch(() => {});
// Configure the additional seMetering attributes for energy tariffs
const meteringAttributes = [
'currentSummReceived',
'currentTier1SummDelivered',
'currentTier2SummDelivered',
'currentTier1SummReceived',
'currentTier2SummReceived'
];
// Read the attributes initially
await endpoint.read('seMetering', meteringAttributes).catch(() => {});
// Configure reporting for energy tariff attributes
for (const attr of meteringAttributes) {
await endpoint.configureReporting('seMetering', [{
attribute: attr,
minimumReportInterval: 300, // Don't report more often than every 5 minutes
maximumReportInterval: 3600, // Report at least every hour
reportableChange: 1000 // Report when energy changes by 1 kWh
}]).catch(() => {});
}
},
};
module.exports = definition; |
|
@Koenkk, fz.electrical_measurement handles cluster haElectricalMeasurement but the part I would like to add in the existing converter is related to the cluster seMetering. |
|
Ok, I tested a new converter (see bellow) with and updated // Support for tariff-based energy measurements (e.g., P1/OBIS smart meters)
if (msg.data.currentTier1SummDelivered !== undefined) {
const value = msg.data.currentTier1SummDelivered;
const property = postfixWithEndpointName("energy_tier1", msg, model, meta);
payload[property] = value * (factor ?? 1);
}
if (msg.data.currentTier2SummDelivered !== undefined) {
const value = msg.data.currentTier2SummDelivered;
const property = postfixWithEndpointName("energy_tier2", msg, model, meta);
payload[property] = value * (factor ?? 1);
}
if (msg.data.currentTier1SummReceived !== undefined) {
const value = msg.data.currentTier1SummReceived;
const property = postfixWithEndpointName("produced_energy_tier1", msg, model, meta);
payload[property] = value * (factor ?? 1);
}
if (msg.data.currentTier2SummReceived !== undefined) {
const value = msg.data.currentTier2SummReceived;
const property = postfixWithEndpointName("produced_energy_tier2", msg, model, meta);
payload[property] = value * (factor ?? 1);
}The converter is now: const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const m = require('zigbee-herdsman-converters/lib/modernExtend');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const e = exposes.presets;
const ea = exposes.access;
const definition = {
zigbeeModel: ['EMIZB-151'],
model: 'EMIZB-151',
vendor: 'Frient',
description: 'Electricity Meter Interface 2 P1',
// Use modern extend for three-phase electricity meter + standard metering converter
extend: [m.electricityMeter({threePhase: true})],
// Add standard fz.metering for energy and tariff data
fromZigbee: [fz.metering],
// Add exposes for the seMetering attributes from fz.metering
exposes: [
e.numeric('energy_tier1', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 1 (peak/high) - OBIS 1.8.1'),
e.numeric('energy_tier2', ea.STATE).withUnit('kWh').withDescription('Energy consumed in tariff 2 (off-peak/low) - OBIS 1.8.2'),
e.numeric('produced_energy', ea.STATE).withUnit('kWh').withDescription('Total energy returned to the grid - OBIS 2.8.0'),
e.numeric('produced_energy_tier1', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 1 (peak/high) - OBIS 2.8.1'),
e.numeric('produced_energy_tier2', ea.STATE).withUnit('kWh').withDescription('Energy produced in tariff 2 (off-peak/low) - OBIS 2.8.2'),
],
ota: true,
endpoint: (device) => ({default: 2}),
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(2);
// Bind seMetering cluster
try {
await reporting.bind(endpoint, coordinatorEndpoint, ['seMetering']);
} catch (error) {
// Binding can fail but device may still work
}
// Read metering multiplier/divisor (required by fz.metering)
await reporting.readMeteringMultiplierDivisor(endpoint).catch(() => {});
// Configure reporting for all energy attributes including tariffs
const meteringAttributes = [
'instantaneousDemand', // power
'currentSummDelivered', // total energy consumed
'currentSummReceived', // total energy produced
'currentTier1SummDelivered', // energy consumed tariff 1 (OBIS 1.8.1)
'currentTier2SummDelivered', // energy consumed tariff 2 (OBIS 1.8.2)
'currentTier1SummReceived', // energy produced tariff 1 (OBIS 2.8.1)
'currentTier2SummReceived' // energy produced tariff 2 (OBIS 2.8.2)
];
// Read the attributes initially
await endpoint.read('seMetering', meteringAttributes).catch(() => {});
// Configure reporting for all metering attributes
for (const attr of meteringAttributes) {
await endpoint.configureReporting('seMetering', [{
attribute: attr,
minimumReportInterval: 300, // Don't report more often than every 5 minutes
maximumReportInterval: 3600, // Report at least every hour
reportableChange: 1000 // Report when energy changes by 1 kWh
}]).catch(() => {});
}
},
};
module.exports = definition;Should I create another another PR for the update of |
Can be updated in this PR. |
Added support for tariff-based energy measurements including tiered energy delivered and received.
Removed try-catch block around reporting.bind call.
Fabiancrg
left a comment
There was a problem hiding this comment.
Hope I did it correctly now
| } | ||
| if (msg.data.currentTier2SummReceived !== undefined) { | ||
| const value = msg.data.currentTier2SummReceived; | ||
| const property = postfixWithEndpointName("produced_energy_tier2", msg, model, meta); |
There was a problem hiding this comment.
| const property = postfixWithEndpointName("produced_energy_tier2", msg, model, meta); | |
| const property = postfixWithEndpointName("produced_energy_tier_2", msg, model, meta); |
(make same change for the others)
There was a problem hiding this comment.
poperty fields updated
|
|
||
| extend: [m.electricityMeter({threePhase: true})], | ||
|
|
||
| fromZigbee: [fz.metering], |
There was a problem hiding this comment.
Should not be needed, added by m.electricityMeter
|
|
||
| await reporting.bind(endpoint, coordinatorEndpoint, ["seMetering"]); | ||
|
|
||
| await reporting.readMeteringMultiplierDivisor(endpoint).catch(() => {}); |
There was a problem hiding this comment.
Could you integrate this into electricityMeter?
There was a problem hiding this comment.
I did an attempt, hope it's good
Removed deprecated EMIZB-151 power meter sensor definition and replaced it with a new electricity meter interface definition.
| } | ||
| if (msg.data.currentTier2SummReceived !== undefined) { | ||
| const value = msg.data.currentTier2SummReceived; | ||
| const property = postfixWithEndpointName("produced_energy_tier2", msg, model, meta); |
There was a problem hiding this comment.
poperty fields updated
|
|
||
| extend: [m.electricityMeter({threePhase: true})], | ||
|
|
||
| fromZigbee: [fz.metering], |
|
|
||
| await reporting.bind(endpoint, coordinatorEndpoint, ["seMetering"]); | ||
|
|
||
| await reporting.readMeteringMultiplierDivisor(endpoint).catch(() => {}); |
There was a problem hiding this comment.
I did an attempt, hope it's good
Fabiancrg
left a comment
There was a problem hiding this comment.
Fix syntax error
|
Thanks! |
|
Looking forward to this update. Many thanks for your work. |
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Koen Kanters <[email protected]>
This is related to new converter file for the existing Frient EMIZB-151 device. Please see the issue bellow with new converter and details about the update: #10457