[New device support]: TOMZN TOB9Z-VAP Smart circuit breaker
Link
https://www.aliexpress.com/item/1005006177598520.html
Database entry
{"id":18,"type":"Router","ieeeAddr":"top_secret","nwkAddr":"top_secret","manufId":4098,"manufName":"_TZ3000_303avxxt","powerSource":"Mains (single phase)","modelId":"TS011F","epList":[1,242],"endpoints":{"1":{"profId":260,"epId":1,"devId":266,"inClusterList":[0,3,4,5,6,1794,2820,1026,57344,57345],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"\u0000\u0000\u0000\u0000\u0005\u001b\t�-\u0013\u001c\t�-\u0012!\t�-\u0018","65506":31,"65508":0,"65534":0,"modelId":"TS011F","manufacturerName":"_TZ3000_303avxxt","stackVersion":0,"dateCode":"","zclVersion":3,"appVersion":69,"powerSource":1}},"genOnOff":{"attributes":{"32768":0,"onOff":1,"onTime":0,"offWaitTime":0,"tuyaBacklightMode":1,"moesStartUpOnOff":2,"tuyaBacklightSwitch":1}},"manuSpecificTuya_3":{"attributes":{"53248":0,"53249":0,"53250":0,"53251":0,"53252":0,"53253":0,"powerOnBehavior":2,"switchType":0}},"haElectricalMeasurement":{"attributes":{"acCurrentDivisor":1000,"acCurrentMultiplier":1,"rmsVoltage":229,"rmsCurrent":0,"activePower":0}},"seMetering":{"attributes":{"divisor":100,"multiplier":1,"currentSummDelivered":[0,0]}},"manuSpecificBosch":{"attributes":{"53251":"AAAA"}},"msTemperatureMeasurement":{"attributes":{"measuredValue":0}}},"binds":[{"cluster":1026,"type":"endpoint","deviceIeeeAddress":"top_secret","endpointID":1},{"cluster":6,"type":"endpoint","deviceIeeeAddress":"top_secret","endpointID":1},{"cluster":2820,"type":"endpoint","deviceIeeeAddress":"top_secret","endpointID":1},{"cluster":1794,"type":"endpoint","deviceIeeeAddress":"top_secret","endpointID":1}],"configuredReportings":[{"cluster":2820,"attrId":1285,"minRepIntval":5,"maxRepIntval":3600,"repChange":1,"manufacturerCode":null},{"cluster":2820,"attrId":1288,"minRepIntval":5,"maxRepIntval":3600,"repChange":10,"manufacturerCode":null},{"cluster":2820,"attrId":1291,"minRepIntval":5,"maxRepIntval":3600,"repChange":1,"manufacturerCode":null},{"cluster":1794,"attrId":0,"minRepIntval":5,"maxRepIntval":3600,"repChange":[1,1],"manufacturerCode":null}],"meta":{}},"242":{"profId":41440,"epId":242,"devId":97,"inClusterList":[],"outClusterList":[33],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":69,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{"configured":332242049},"lastSeen":1715873912168}
Comments
Hello. Please, add support for TOMZN TOB9Z-VAP Smart circuit breaker.
I was able to put together an external converter for this device, see external definition section.
Observations
- unlike similar models from EARU EAKCB-T-M-Z and Tongou TO-Q-SY2-163JZT, this circuit breaker does not measure temperature, nor does it provide temp breaker and threshold (also no power breaker and threshold)
- energy reading is reset to 0 after the circuit breaker is removed from Z2M and then added again
Here is an image of the device from the official product page:
External definition
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 modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const utils = require('zigbee-herdsman-converters/lib/utils');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const { Buffer } = require('node:buffer');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const fzLocal = {
TS011F_electrical_measurement: {
...fz.electrical_measurement,
convert: async (model, msg, publish, options, meta) => {
const result = await fz.electrical_measurement.convert(model, msg, publish, options, meta) ?? {};
const lookup = {power: 'activePower', current: 'rmsCurrent', voltage: 'rmsVoltage'};
// Wait 5 seconds before reporting a 0 value as this could be an invalid measurement.
// https://github.com/Koenkk/zigbee2mqtt/issues/16709#issuecomment-1509599046
if (result) {
for (const key of ['power', 'current', 'voltage']) {
if (key in result) {
const value = result[key];
clearTimeout(globalStore.getValue(msg.endpoint, key));
if (value === 0) {
const configuredReporting = msg.endpoint.configuredReportings.find((c) =>
c.cluster.name === 'haElectricalMeasurement' && c.attribute.name === lookup[key]);
const time = ((configuredReporting ? configuredReporting.minimumReportInterval : 5) * 2) + 1;
globalStore.putValue(msg.endpoint, key, setTimeout(() => {
const payload = {[key]: value};
// Device takes a lot of time to report power 0 in some cases. When current == 0 we can assume power == 0
// https://github.com/Koenkk/zigbee2mqtt/discussions/19680#discussioncomment-7868445
if (key === 'current') {
payload.power = 0;
}
publish(payload);
}, time * 1000));
delete result[key];
}
}
}
}
// Device takes a lot of time to report power 0 in some cases. When the state is OFF we can assume power == 0
// https://github.com/Koenkk/zigbee2mqtt/discussions/19680#discussioncomment-7868445
if (meta.state.state === 'OFF') {
result.power = 0;
}
return result;
}
},
TS011F_threshold: {
cluster: 'manuSpecificTuya_3',
type: 'raw',
convert: (model, msg, publish, options, meta) => {
const splitToAttributes = (value) => {
const result = {};
const len = value.length;
let i = 0;
while (i < len) {
const key = value.readUInt8(i);
result[key] = [value.readUInt8(i+1), value.readUInt16BE(i+2)];
i += 4;
}
return result;
};
const lookup = {0: 'OFF', 1: 'ON'};
const command = msg.data[2];
const data = msg.data.slice(3);
if (command == 0xE7) {
const value = splitToAttributes(data);
return {
'over_current_threshold': value[0x01][1],
'over_current_breaker': lookup[value[0x01][0]],
'over_voltage_threshold': value[0x03][1],
'over_voltage_breaker': lookup[value[0x03][0]],
'under_voltage_threshold': value[0x04][1],
'under_voltage_breaker': lookup[value[0x04][0]],
};
}
}
}
};
const tzLocal = {
TS011F_threshold: {
key: [
'over_current_threshold', 'over_current_breaker', 'over_voltage_threshold', 'over_voltage_breaker',
'under_voltage_threshold', 'under_voltage_breaker',
],
convertSet: async (entity, key, value, meta) => {
const onOffLookup = {'on': 1, 'off': 0};
switch (key) {
case 'over_current_threshold': {
const state = meta.state['over_current_breaker'];
const buf = Buffer.from([1, utils.getFromLookup(state, onOffLookup), 0, utils.toNumber(value, 'over_current_threshold')]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
case 'over_current_breaker': {
const threshold = meta.state['over_current_threshold'];
const number = utils.toNumber(threshold, 'over_current_threshold');
const buf = Buffer.from([1, utils.getFromLookup(value, onOffLookup), 0, number]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
case 'over_voltage_threshold': {
const state = meta.state['over_voltage_breaker'];
const buf = Buffer.from([3, utils.getFromLookup(state, onOffLookup), 0, utils.toNumber(value, 'over_voltage_breaker')]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
case 'over_voltage_breaker': {
const threshold = meta.state['over_voltage_threshold'];
const number = utils.toNumber(threshold, 'over_voltage_threshold');
const buf = Buffer.from([3, utils.getFromLookup(value, onOffLookup), 0, number]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
case 'under_voltage_threshold': {
const state = meta.state['under_voltage_breaker'];
const buf = Buffer.from([4, utils.getFromLookup(state, onOffLookup), 0, utils.toNumber(value, 'under_voltage_threshold')]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
case 'under_voltage_breaker': {
const threshold = meta.state['under_voltage_threshold'];
const number = utils.toNumber(threshold, 'under_voltage_breaker');
const buf = Buffer.from([4, utils.getFromLookup(value, onOffLookup), 0, number]);
await entity.command('manuSpecificTuya_3', 'setOptions3', {data: buf});
break;
}
default: // Unknown key
logger.warning(`Unhandled key ${key}`, NS);
}
}
}
};
const definition = {
fingerprint: tuya.fingerprint('TS011F', ['_TZ3000_303avxxt']),
model: 'TS011F_with_threshold',
description: 'Din rail switch with power monitoring and threshold settings',
vendor: 'TuYa',
ota: ota.zigbeeOTA,
extend: [tuya.modernExtend.tuyaOnOff({
electricalMeasurements: true, electricalMeasurementsFzConverter: fzLocal.TS011F_electrical_measurement,
powerOutageMemory: true, indicatorMode: true,
})],
fromZigbee: [fz.temperature, fzLocal.TS011F_threshold],
toZigbee: [tzLocal.TS011F_threshold],
exposes: [
e.numeric('over_current_threshold', ea.STATE_SET).withValueMin(1).withValueMax(64).withValueStep(1).withUnit('A')
.withDescription('Over-current threshold'),
e.binary('over_current_breaker', ea.STATE_SET, 'ON', 'OFF')
.withDescription('Over-current breaker'),
e.numeric('over_voltage_threshold', ea.STATE_SET).withValueMin(220).withValueMax(265).withValueStep(1).withUnit('V')
.withDescription('Over-voltage threshold'),
e.binary('over_voltage_breaker', ea.STATE_SET, 'ON', 'OFF')
.withDescription('Over-voltage breaker'),
e.numeric('under_voltage_threshold', ea.STATE_SET).withValueMin(76).withValueMax(240).withValueStep(1).withUnit('V')
.withDescription('Under-voltage threshold'),
e.binary('under_voltage_breaker', ea.STATE_SET, 'ON', 'OFF')
.withDescription('Under-voltage breaker'),
],
configure: async (device, coordinatorEndpoint) => {
await tuya.configureMagicPacket(device, coordinatorEndpoint);
const endpoint = device.getEndpoint(1);
endpoint.command('genBasic', 'tuyaSetup', {});
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
await reporting.rmsVoltage(endpoint, {change: 1});
await reporting.rmsCurrent(endpoint, {change: 10});
await reporting.activePower(endpoint, {change: 1});
await reporting.currentSummDelivered(endpoint);
endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {acCurrentDivisor: 1000, acCurrentMultiplier: 1});
endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
device.save();
},
whiteLabel: [
tuya.whitelabel('TOMZN', 'TOB9Z-VAP', 'Smart circuit breaker', ['_TZ3000_303avxxt']),
],
};
module.exports = definition;
hey, @lordlightman I've bought the same device and encountered the same issue.
I'm thinking about writing zha quirk and add it to my hass, but I'm new to this. Are there any way I could adapt your code above to python zha quirk file?
Hello @ogvalt. Sorry, I am not a developer, I barely scraped this converter together following various tutorials on how to write an external converter for Z2M.
@lordlightman what you scraped together is quite impressive!
Thanks @ogvalt, I just took bits and pieces from Tuya converter for similar device EARU EAKCB-T-M-Z and after some trial and error managed to make my external converter work.
@lordlightman it seems I also successfully found a solution and everything working as I expected
could you make a pull request to add out of the box support for this device?
Hello @Koenkk. Sorry, I'm not a developer, I do not know how to integrate my converter into the file with all other Tuya converters.
@lordlightman just to let you know I added this converter (using GUI) with latest Z2M (1.39.0-1) but converter is not starting firing an error
[2024-07-02 00:25:01] info: z2m: Logging to console, file (filename: log.log)
[2024-07-02 00:25:01] error: z2m: Failed to load external converter file blabla
[2024-07-02 00:25:01] error: z2m: Probably there is a syntax error in the file or the external converter is not compatible with the current Zigbee2MQTT version
anyway, not a big deal but could be great to see this device integrated to Z2M :-)
BTW, not related I also added this device to Tuya Smart Home (with Tuya ZigBee HUB) and I don't have any option (in Tuya App) to set threshold for over voltage or over current, very strange because it is the same device of the picture. So if someone knows how to set these option with native app, let me know please.
I also have no luck using over voltage/current protection via zha. it seems that device do not expose corresponding endpoints.
so I would say it's kinda misleading marketing from their side, because i specifically chose this device over cheaper one
@ogvalt did you install the external decoder in ZHA? also did you try with official tuya app just to be sure?
Anyway I bought them also for this feature and I'm unable to have the menu below with official app
@hallard I haven't tried and I'm unable to, because my setup is custom. Also no external decoder.
My observations are based on observations from home assistant logs. I found zha device descriptor that matched with endpoints that device was broadcasting. Maybe they yet not exposed this features on firmware level?
@ogvalt got it, I think to have it on HA you need the custom decoder of this issue (if your device can handle it of course), may explain why you don't have it, worth trying maybe :-)
@Koenkk, you confirm this change is not merged due to lack of PR?
you confirm this change is not merged due to lack of PR?
yes
Hello @hallard, sorry to hear that you got an error. I've updated to Zigbee2MQTT version 1.39.0 this week and did not get any errors in the logs - the converter loads and works fine.
I sent back devices even with official Tuya app I don't have these settings, I suspect marketing issue 🤣😂
@hallard, unfortunately, I cannot provide any help with the Tuya app since I'm only using Z2M. You can contact TOMZN support on AliExpress to inquire about the thresholds in the app. Also, are you certain that your model is TOB9Z-VAP? There are also 2 other models - TOB9Z-M with metering and TOB9Z-63T which is just a switch. If you purchased this device on AliExpress, you may open a dispute and get your money back if they shipped you the wrong device or it lacks the functions mentioned on the product page. Perhaps, the device is just defective or the manufacturer put a wrong label on it. Not related to this device, but I've purchased a number of dumb voltage protection devices from TOMZN, and sometimes their settings and functions were a bit different even though the model number was the same.
Sorry it was just for information no need for support ;) Yep it's the correct model and I already asked tomzn on Ali they are taking me for a noob and says it works (ah ah for sure no options on my side) so I asked for a refund May be device in enclosure do not match name on enclosure
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days
I apologize for the off-topic question, but does the relay have a time delay function? When starting, should it wait for a set time before supplying power to the load?