fix: TS011F dynamic multi-gang support and metering via electricityMeter#12606
fix: TS011F dynamic multi-gang support and metering via electricityMeter#12606hml96 wants to merge 1 commit into
Conversation
|
The Tuya socket definitions have become fragmented and inconsistent across many contributors. We are consolidating them under the main TS011F model definition, and removing the faulty fingerprint matches that rely on modelID + applicationVersion. |
| { | ||
| fingerprint: [ | ||
| {modelID: "TS011F", applicationVersion: 192, manufacturerName: "_TZ3000_2uollq9d", priority: -1}, | ||
| {modelID: "TS011F", applicationVersion: 160, priority: -1}, |
There was a problem hiding this comment.
This will remove polling for those devices (and thus break electricity measurements). I think it's better to support multi-gang definitions with a separate definition.
There was a problem hiding this comment.
Fingerprint matching based on modelID + applicationVersion is unreliable by design. In Tuya's product line, metering sockets and power strips — whether single-gang or multi-gang, and regardless of the chip platform — all use TS011F as the model ID. The unified handling logic should therefore live in the single TS011F definition (TS011F_plug_1).
Matching on modelID + applicationVersion to route certain devices to a separate definition shouldn't exist in the first place: applicationVersion is not a stable product identifier — the same value can appear on completely unrelated products, and a firmware update can change it on the same product. When a specific device genuinely needs divergent logic, the correct way to single it out is modelID + manufacturerName.
There was a problem hiding this comment.
I agree with that, ideally go with modelId + manuf + appVersion, but that doesn't change that the appVersion 69 plugs are not polled anymore (which will break the energy reporting for those)
There was a problem hiding this comment.
Tuya smart sockets feature a scheduled automatic reporting function, so energy reporting capabilities are not completely lost.
There was a problem hiding this comment.
A large number of other users are running the same appVersion, and we have received extensive feedback, so we believe it is necessary to correct this.
There was a problem hiding this comment.
The official firmware for Tuya sockets certainly includes a scheduled reporting function. If a device is observed failing to report, there are two possibilities: first, a failure in tuya.modernExtend.tuyaBase()—specifically, if the SDK fails to receive the necessary read command during network joining or power-up, it will disable the active reporting capability; second, a third-party developer using our SDK for custom development may have disabled the active reporting synchronization feature. However, we believe that in either case, this affects only a small number of devices and should not impact the majority.
There was a problem hiding this comment.
extend: [tuya.modernExtend.tuyaBase(), m.identify(), m.electricityMeter()],
fromZigbee: [
fz.on_off,
tuya.fz.on_off_countdown,
tuya.fz.power_outage_memory,
tuya.fz.child_lock,
tuya.fz.indicator_mode,
tuya.fz.switch_type_button,
],
toZigbee: [
tz.on_off,
tuya.tz.on_off_countdown,
tuya.tz.power_on_behavior_1,
tuya.tz.child_lock,
tuya.tz.backlight_indicator_mode_1,
tuya.tz.switch_type_button,
],
// Dynamic multi-gang: first endpoint stays as unsuffixed "state"; the rest become CH<ID>.
// A single function controls ordering: state -> CH2..CHN -> features.
exposes: (device) => {
const eps = !utils.isDummyDevice(device) ? device.endpoints.filter((ep) => ep.supportsInputCluster("genOnOff")) : [];
const mfr = device?.manufacturerName;
const feats = [];
feats.push(e.switch()); // first endpoint = state
for (const ep of eps.slice(1)) feats.push(e.switch().withEndpoint("CH" + ep.ID)); // CH2..CHN
// Features (mirrors the vendor-specific conditions of the original plug_1)
feats.push(tuya.exposes.countdown().withAccess(ea.ALL)); // first endpoint -> countdown
for (const ep of eps.slice(1))
feats.push(
tuya.exposes
.countdown()
.withAccess(ea.ALL)
.withEndpoint("CH" + ep.ID),
); // CH2..CHN -> countdown_CHn
feats.push(tuya.exposes.powerOutageMemory());
feats.push(tuya.exposes.indicatorMode());
feats.push(e.child_lock());
feats.push(tuya.exposes.switchTypeButton());
return feats;
},
endpoint: (device) => {
const map: {[s: string]: number} = {};
const eps = device?.endpoints ? device.endpoints.filter((ep) => ep.supportsInputCluster("genOnOff")) : [];
for (const ep of eps.slice(1)) map["CH" + ep.ID] = ep.ID;
return map;
},
meta: {
multiEndpoint: true,
// Device-level attributes must not get an endpoint suffix.
// Do NOT list "state" here: multi-gang switching relies on "state" being suffixed per endpoint.
multiEndpointSkip: ["power", "current", "voltage", "energy", "child_lock", "power_outage_memory", "indicator_mode", "switch_type"],
},
configure: async (device, coordinatorEndpoint) => {
await tuya.configureMagicPacket(device, coordinatorEndpoint);
const endpoint = device.getEndpoint(1);
await reporting.bind(endpoint, coordinatorEndpoint, ["genOnOff", "haElectricalMeasurement", "seMetering"]);
await reporting.currentSummDelivered(endpoint);
await reporting.rmsVoltage(endpoint, {change: 5});
await reporting.rmsCurrent(endpoint, {change: 50});
// Fallback scaling factors, used only when the device does not report its own
// multipliers/divisors. m.electricityMeter() reads them from the device and,
// when successful, overrides the values saved here.
endpoint.saveClusterAttributeKeyValue("haElectricalMeasurement", {
acVoltageMultiplier: 1,
acVoltageDivisor: 1,
acPowerMultiplier: 1,
acPowerDivisor: 1,
acCurrentDivisor: 1000,
acCurrentMultiplier: 1,
});
endpoint.saveClusterAttributeKeyValue("seMetering", {
divisor: 100,
multiplier: 1,
});
utils.attachOutputCluster(device, "genOta");
device.save();
},
},Alternatively, I could synchronize this logic with the existing modelID + applicationVersion matching logic without disrupting the original polling mechanism; do you think that would be feasible?
There was a problem hiding this comment.
first, a failure in tuya.modernExtend.tuyaBase()—specifically, if the SDK fails to receive the necessary read command during network joining or power-up
For my device the configure passes, (we call it the "tuya magic packet"), so this cannot be the case.
a third-party developer using our SDK for custom development may have disabled the active reporting synchronization feature
I bought from AliExpress, I guess that should come with the official fw?
However, we believe that in either case, this affects only a small number of devices and should not impact the majority.
I think quite some devices will be impacted (given the list of fingerprints that have polling enabled). For all of these devices, polling was enabled after we figured out they do not report values themselves.
There was a problem hiding this comment.
Could you help me review the new submission?
There was a problem hiding this comment.
I see the polling is still not present, it would be good if you could provide an external converter such I can test if it works with my plug without polling?
Add a new TS011F_Socket definition matched by manufacturerName for multi-gang power monitoring sockets. Detects genOnOff endpoints at runtime: first endpoint stays unsuffixed (state), additional endpoints become CH2..CHN. Metering via m.electricityMeter(). Tuya-specific features (countdown per gang, power-outage memory, indicator mode, child lock, switch type) included. Restore the version-only fingerprints in TS011F_plug_3 (previously removed) with priority:-1 so they remain lower priority than any manufacturerName-based fingerprint (priority 0).
0a081a3 to
f34629b
Compare
|
I have made further revisions; please review it again. |
Link to picture pull request: TODO