zigbee2mqtt icon indicating copy to clipboard operation
zigbee2mqtt copied to clipboard

[New device support]: Bosch Smart Home Twinguard Smoke Detector

Open safakaltun opened this issue 2 years ago • 16 comments

Link

https://www.bosch-smarthome.com/uk/en/products/devices/twinguard/

Database entry

{"id":76,"type":"EndDevice","ieeeAddr":"0x000d6f00179e01b9","nwkAddr":43776,"manufId":4617,"manufName":"BoschSmartHomeGmbH","powerSource":"Battery","modelId":"Champion","epList":[1,2,3,4,5,6,7,8,9,10,11,12],"endpoints":{"1":{"profId":260,"epId":1,"devId":96,"inClusterList":[0,3,4,9,57344,57348],"outClusterList":[3,9],"clusters":{"genBasic":{"attributes":{"manufacturerName":"BoschSmartHomeGmbH"}}},"binds":[],"configuredReportings":[],"meta":{}},"2":{"profId":260,"epId":2,"devId":97,"inClusterList":[0,3,4,9,57345,57348],"outClusterList":[3,9],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}},"3":{"profId":260,"epId":3,"devId":61440,"inClusterList":[0,3,57346],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}},"4":{"profId":260,"epId":4,"devId":98,"inClusterList":[0,1,3,4,9],"outClusterList":[3,9],"clusters":{"genBasic":{"attributes":{}},"genPowerCfg":{"attributes":{"batteryVoltage":91}}},"binds":[{"cluster":1,"type":"endpoint","deviceIeeeAddress":"0x00124b0024c11416","endpointID":1}],"configuredReportings":[{"cluster":1,"attrId":32,"minRepIntval":60,"maxRepIntval":"62000","repChange":0}],"meta":{}},"5":{"profId":260,"epId":5,"devId":770,"inClusterList":[0,3,1026],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}},"msTemperatureMeasurement":{"attributes":{"measuredValue":2540}}},"binds":[{"cluster":1026,"type":"endpoint","deviceIeeeAddress":"0x00124b0024c11416","endpointID":1}],"configuredReportings":[{"cluster":1026,"attrId":0,"minRepIntval":"1","maxRepIntval":3600,"repChange":"100"}],"meta":{}},"6":{"profId":260,"epId":6,"devId":262,"inClusterList":[0,3,1024],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}},"7":{"profId":260,"epId":7,"devId":6,"inClusterList":[0,3,32],"outClusterList":[3,10,25],"clusters":{"genBasic":{"attributes":{}},"genPollCtrl":{"attributes":{"checkinInterval":6480}}},"binds":[{"cluster":32,"type":"endpoint","deviceIeeeAddress":"0x00124b0024c11416","endpointID":1}],"configuredReportings":[],"meta":{}},"8":{"profId":260,"epId":8,"devId":1012,"inClusterList":[0,3,1027],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}},"msPressureMeasurement":{"attributes":{"measuredValue":30603}}},"binds":[],"configuredReportings":[],"meta":{}},"9":{"profId":260,"epId":9,"devId":1008,"inClusterList":[0,3,1029],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}},"msRelativeHumidity":{"attributes":{"measuredValue":4787}}},"binds":[{"cluster":1029,"type":"endpoint","deviceIeeeAddress":"0x00124b0024c11416","endpointID":1}],"configuredReportings":[{"cluster":1029,"attrId":0,"minRepIntval":"1","maxRepIntval":3600,"repChange":"100"}],"meta":{}},"10":{"profId":260,"epId":10,"devId":61442,"inClusterList":[0,3,57349],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}},"11":{"profId":260,"epId":11,"devId":1010,"inClusterList":[0,3,1036],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}},"12":{"profId":260,"epId":12,"devId":1011,"inClusterList":[0,3,57347,57350,57351],"outClusterList":[3],"clusters":{"genBasic":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}}},"hwVersion":8,"dateCode":"20220210","swBuildId":"6b6048b","zclVersion":1,"interviewCompleted":true,"meta":{"configured":269791260},"lastSeen":1664994575645,"defaultSendRequestWhen":"immediate"}

Comments

Hi,

I have been trying to add support for this device, but haven't progressed a lot. I managed to read the temperature, humidity and the battery voltage, but couldn't go any further than that. I tried using the existing converters implemented in similar devices, but couldn't make any of them work. Unfortunately I hit a point where I cannot progress any more since I don't know what else I can try.

According to the OpenAPI documentation, it should at least report the following signals as well as the smoke detection status and a smoke test trigger.

"state": { "combinedRating": [ "GOOD", "MEDIUM", "BAD" ], "description": "string", "temperature": 0, "temperatureRating": [ "GOOD", "MEDIUM", "BAD" ], "humidity": 0, "humidityRating": [ "GOOD", "MEDIUM", "BAD" ], "purity": 0, "purityRating": "string"

According to the product page, it should also be possible to trigger its built-in siren. To test this feature, I tried "squawk" and "warning", but the device did not respond.

External converter

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 extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['Champion'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    model: 'Twinguard', // Vendor model number, look on the device for a model number
    vendor: 'Bosch', // Vendor of the device (only used for documentation and startup logging)
    description: 'Twinguard', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fz.temperature, fz.humidity, fz.battery, fz.ias_siren, fz.ias_smoke_alarm_1],
    toZigbee: [tz.warning, tz.squawk],
	meta: {battery: {voltageToPercentage: '9V_2500'}},
    configure: async (device, coordinatorEndpoint, logger) => {
		device.defaultSendRequestWhen = 'immediate';
		device.type = 'EndDevice';
        device.save();
        await device.getEndpoint(1).unbind('genPollCtrl', coordinatorEndpoint);
    },
    exposes: [e.warning(), e.temperature(), e.humidity(), e.battery(), e.squawk(), e.smoke()],
};

module.exports = definition;

Supported color modes

No response

Color temperature range

No response

safakaltun avatar Oct 05 '22 20:10 safakaltun

I am not sure why my database entry is this long.

safakaltun avatar Oct 05 '22 20:10 safakaltun

@Koenkk do you have any suggestions I can try?

safakaltun avatar Oct 21 '22 12:10 safakaltun

I see the device has a lot of endpoints, by default it will send to 1 but there are more: "epList":[1,2,3,4,5,6,7,8,9,10,11,12]. Can you try the commands on all the eps? You can do that with e.g. zigbee2mqtt/FRIENDLY_NAME/1/set

Koenkk avatar Oct 22 '22 07:10 Koenkk

I have tried this via the Dev console and I am able to read from cluster 5 result of 'msTemperatureMeasurement': {"measuredValue":2288}. Which updates the temperature value exposed from the device. However, it does not automatically update this value. I can also read humidity from cluster 9.

safakaltun avatar Oct 22 '22 15:10 safakaltun

Try changing the configure to this:

    configure: async (device, coordinatorEndpoint, logger) => {
        await reporting.temperature(device.getEndpoint(5));
        await reporting.humidity(device.getEndpoint(9));
    },

Koenkk avatar Oct 22 '22 15:10 Koenkk

Unfortunately nothing happens. I re-paired one of the devices after this change, and hasn't reported anything since. Both temperature and humidity shows N/A or Null.

I also reduced the configuration to expose only temperature and humidity:

const definition = {
    zigbeeModel: ['Champion'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    model: 'Twinguard', // Vendor model number, look on the device for a model number
    vendor: 'Bosch ST', // Vendor of the device (only used for documentation and startup logging)
    description: 'Twinguard', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fz.temperature, fz.humidity],
    toZigbee: [],
    configure: async (device, coordinatorEndpoint, logger) => {
        await reporting.temperature(device.getEndpoint(5));
        await reporting.humidity(device.getEndpoint(9));
    },
    exposes: [e.temperature(), e.humidity()],
};

safakaltun avatar Oct 22 '22 17:10 safakaltun

I forgot to add the binding part:

    configure: async (device, coordinatorEndpoint, logger) => {
        await reporting.bind(device.getEndpoint(5), coordinatorEndpoint, ['msTemperatureMeasurement']);
        await reporting.bind(device.getEndpoint(9), coordinatorEndpoint, ['msRelativeHumidity']);
        await reporting.temperature(device.getEndpoint(5));
        await reporting.humidity(device.getEndpoint(9));
    },

Koenkk avatar Oct 23 '22 07:10 Koenkk

I can see the binding taking effect, but unfortunately the device still does not update the values. After changing the configuration file and re-pairing the device, both temperature and humidity shows Null. I can still manually get the values. I wonder if the device expects the coordinator ask for an update or something.

safakaltun avatar Oct 24 '22 14:10 safakaltun

That's strange, do you have the original hub and if yes, can you sniff the traffic when pairing the device to it?

https://www.zigbee2mqtt.io/advanced/zigbee/04_sniff_zigbee_traffic.html#with-cc2531

Koenkk avatar Oct 24 '22 17:10 Koenkk

Hi,

I have finally got the time to sniff the traffic while pairing with its hub - please see the attachment. Bosch Hub seems to be using a network key that I am unable to find what.

twinguard_firsttime_join_BSMC.zip

safakaltun avatar Nov 22 '22 19:11 safakaltun

Is this device paired through an install code?

Koenkk avatar Nov 23 '22 16:11 Koenkk

Yes

safakaltun avatar Nov 23 '22 17:11 safakaltun

Here is the content of the install code just in case

RB01SG0D836591B3CC0010000000000000000000000D6F00179E01B9DLK6B0670B9EE705E9B3E3271D1B90B1A8F

safakaltun avatar Nov 23 '22 18:11 safakaltun

Is it also possible to pair this device without the install code? I don't know how to converter the install code to a network key. I'm looking for a Transport Key message in the sniff.

Koenkk avatar Nov 24 '22 15:11 Koenkk

It is not possible to pair the device without an install code. However, one can manually add the device without the install code but with what Bosch calls “Device Local Key”. It is different than the install code. I am attaching a picture of the QR code, and the device local key from behind the device.

F21C8F6C-D265-47F4-A460-4A49CB25DCA6

safakaltun avatar Nov 24 '22 15:11 safakaltun

What happens when you permit joining and pair any other random Zigbee devices? Maybe it triggers the transport key message. Without the key I cannot help further.

Koenkk avatar Nov 24 '22 16:11 Koenkk

I could not pair another random device, but I managed to get the transport key for the second unit I have by setting the install key before I start sniffing. I don't know why it made a difference, but anyways. Here is the sniff: Pairing_2BC9.zip

It makes 8 bind requests and successfully completes them. However, 5 of them are shown as unknown clusters. Could you please have a look at the sniff and help me with what to do next?

safakaltun avatar Dec 01 '22 21:12 safakaltun

I have recorded another session to make the sniff a bit cleaner by disconnecting all other devices and pair only a single twinguard.

A0801_pairing_uninterrupted.zip

I found some interesting stuff going on. The device does not report any of the measurements through the existing clusters. Instead, we get a single report every 5 minutes containing all measurements in various attributes. The report looks like this:

image

I went back to see how does the binding is done and saw that it is done using the following clusters from the endpoints we didn't even consider.

| Source Endpoint | Destination Endpoint | Cluster | 1 | 2 | Cluster: 0x0009 (Alarms) | 7 | 2 | Cluster: 0x0019 (OTA Upgrade) | 7 | 2 | Cluster: 0x0020 (Poll Control) | 1 | 2 | Cluster: 0xe000 (Unknown Cluster) | 3 | 2 | Cluster: 0xe002 (Unknown Cluster) | 1 | 2 | Cluster: 0xe004 (Unknown Cluster) | 12 | 2 | Cluster: 0xe006 (Unknown Cluster) | 12 | 2 | Cluster: 0xe007 (Unknown Cluster)

Almost all measurements are coming from 0xe002. I managed to find which attribute corresponds to which measurement for some of the attributes, but I have no clue what the rest is.

attribute 0x4000 humidity attribute 0x4003 attribute 0x4004 temperature attribute 0x4005 illuminance attribute 0x4006 could be battery attribute 0x4007 attribute 0x4009 could be purity level attribute 0x400a attribute 0x400c

safakaltun avatar Dec 02 '22 15:12 safakaltun

Can you try to bind the cluster in the configure and see if you can No converter available.. messages in the debug log?

const endpoint = device.getEndpoint(1);
await reporting.bind(endpoint, coordinatorEndpoint, [0xe002]);

See https://www.zigbee2mqtt.io/guide/usage/debug.html on how to enable debug logging.

Koenkk avatar Dec 02 '22 17:12 Koenkk

z2m identifies 0xe002 as manuSpecificTuya_2. However, as you guessed, I am bombarded with No converter available... messages.

image

safakaltun avatar Dec 02 '22 21:12 safakaltun

also any attribute report messages? (like with the original gateway)

Koenkk avatar Dec 03 '22 09:12 Koenkk

Seems like it does receive a message with the same attributes

image

safakaltun avatar Dec 03 '22 15:12 safakaltun

What do you think about the below code snippet? I am trying to put together a custom converter, but I am not sure if the way I am doing is the best. Also, I am not sure how to expose airpurity which does not seem to exist in the exposes.js.

const fzLocal = {
    bosch_twinguard_alarm: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
			if (msg.data.hasOwnProperty(0x4000)) {
                result.humidity = precisionRound(msg.data[0x4000], 2) / 100.0;
            }
			if (msg.data.hasOwnProperty(0x4003)) {
                result.airpurity = msg.data[0x4003] * 10.0 + 500.0 ;
            }
			if (msg.data.hasOwnProperty(0x4004)) {
                result.temperature = precisionRound(msg.data[0x4004], 2) / 100.0 ;
            }
			if (msg.data.hasOwnProperty(0x4005)) {
                result.illuminance_lux = msg.data[0x4005];
            }
            return result;
        },
    },
};

const definition = {
    zigbeeModel: ['Champion'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    model: 'Twinguard', // Vendor model number, look on the device for a model number
    vendor: 'Bosch ST', // Vendor of the device (only used for documentation and startup logging)
    description: 'Twinguard', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fzLocal.bosch_twinguard_alarm],
    toZigbee: [],
    configure: async (device, coordinatorEndpoint, logger) => {
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0x0009]);
		await reporting.bind(device.getEndpoint(7), coordinatorEndpoint, [0x0019]);
		await reporting.bind(device.getEndpoint(7), coordinatorEndpoint, [0x0020]);
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0xe000]);
		await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, [0xe002]);
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0xe004]);
		await reporting.bind(device.getEndpoint(12), coordinatorEndpoint, [0xe006]);
		await reporting.bind(device.getEndpoint(12), coordinatorEndpoint, [0xe007]);
    },
    exposes: [e.temperature(), e.humidity(), e.illuminance_lux],
};

safakaltun avatar Dec 03 '22 18:12 safakaltun

The previous converter did not work, but what I have below seems to be working.

For some reason though, the attributes are received as decimals instead of hexadecimals. So I needed to convert the hex values. Any suggestions on why this could be the case?

I also noticed that the device does not pair correctly. Although it still reports the values every 5 minutes, it starts beeping if it disconnects from the z2m network and does not stop beeping even if it can connect back to the network. can you think of anything that can cause this?

const fzLocal = {
    bosch_twinguard_alarm: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport', 'readResponse'],
		options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature'),
        exposes.options.precision('humidity'), exposes.options.calibration('humidity'),
		exposes.options.calibration('illuminance_lux', 'percentual')],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
			if (msg.data.hasOwnProperty(16384)) {
//            result.humidity = msg.data['0x4000'] / 100.0 ;
                result.humidity = msg.data['16384'] / 100.0 ;
            }
			if (msg.data.hasOwnProperty(16393)) {
//            result.airpurity = msg.data['0x4003'] * 10.0 + 500.0 ;
                result.airpurity = msg.data['16393'] * 10.0 + 500.0 ;
            }
			if (msg.data.hasOwnProperty(16388)) {
//            result.temperature = msg.data['0x4004'] / 100.0 ;
                result.temperature = msg.data['16388'] / 100.0 ;
            }
			if (msg.data.hasOwnProperty(16389)) {
//            result.illuminance_lux = msg.data['0x4005'] / 2.0 ;
                result.illuminance_lux = msg.data['16389'] / 2.0 ;
            }
			return result;
        },
    },
};

const definition = {
    zigbeeModel: ['Champion'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    model: 'Twinguard', // Vendor model number, look on the device for a model number
    vendor: 'Bosch ST', // Vendor of the device (only used for documentation and startup logging)
    description: 'Twinguard', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fzLocal.bosch_twinguard_alarm],
    toZigbee: [],
    configure: async (device, coordinatorEndpoint, logger) => {
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0x0009]);
		await reporting.bind(device.getEndpoint(7), coordinatorEndpoint, [0x0019]);
		await reporting.bind(device.getEndpoint(7), coordinatorEndpoint, [0x0020]);
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0xe000]);
		await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, [0xe002]);
		await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, [0xe004]);
		await reporting.bind(device.getEndpoint(12), coordinatorEndpoint, [0xe006]);
		await reporting.bind(device.getEndpoint(12), coordinatorEndpoint, [0xe007]);
    },
    exposes: [
		exposes.numeric('humidity', ea.STATE).withUnit('%'),
		exposes.numeric('airpurity', ea.STATE).withUnit('ppm'),
		exposes.numeric('temperature', ea.STATE).withUnit('°C'),
		exposes.numeric('illuminance_lux', ea.STATE).withUnit('lx'),
		],
};

safakaltun avatar Dec 03 '22 20:12 safakaltun

The external converter looks good, but I suggest to rename airpurity to co2 or co (whatever is measured)

For some reason though, the attributes are received as decimals instead of hexadecimals. So I needed to convert the hex values. Any suggestions on why this could be the case?

You mean the attributes?

I also noticed that the device does not pair correctly. Although it still reports the values every 5 minutes, it starts beeping if it disconnects from the z2m network and does not stop beeping even if it can connect back to the network. can you think of anything that can cause this?

Can you make a sniff of this?

Koenkk avatar Dec 04 '22 18:12 Koenkk

The external converter looks good, but I suggest to rename airpurity to co2 or co (whatever is measured)

I need to have a better look at what the device reports. In the Bosch Smart Home app, they define it as air purity with the unit ppm. The closest available export is voc, but it has a different unit. I managed to find the sensor used in the twinguard and its datasheet, but that will take some time to read. For the time being I will keep it as it is, but change it before pushing it to the repository.

You mean the attributes?

Yes. In the sniff, I see the attributes as hexadecimals. However, z2m sees them as decimals.

Can you make a sniff of this?

Here is the sniff. Unfortunately I couldn't isolate the sensor, so it is a very big sniff containing a lot of irrelevant communication with other devices. The device's network address is 0x44F6, it might help filtering out other stuff. If you have any suggestions on how I can make the sniff cleaner, please let me know and I can try.

Pairing to z2m A0801_pairing_Z2m.zip Pairing to Bosch controller A0801_pairing_uninterrupted.zip

safakaltun avatar Dec 06 '22 16:12 safakaltun

Yes. In the sniff, I see the attributes as hexadecimals. However, z2m sees them as decimals.

Node.js logs numbers as decimal, but msg.data.hasOwnProperty(16384) will work the same as msg.data.hasOwnProperty(0x4000)

Here is the sniff. Unfortunately I couldn't isolate the sensor, so it is a very big sniff containing a lot of irrelevant

Can you provide the No. (first column) where it disconnects from z2m.

Koenkk avatar Dec 06 '22 17:12 Koenkk

Okay, in that sniff the device had just connected to the network. So the device was reporting as expected. However, I kept sniffing after to capture the communication when the device enters into the failure mode which does not happen using the Bosch's own hub.

Here is the sniff where it enters into the sort of failure mode and starts beeping. beeping.zip

I cannot exactly pinpoint, but it starts after No.21304. Following 21304, it reports attributes 3 times within seconds (No. 21313 and 21316), which is unusual. Normally the device reports once every 5 minutes. 20 seconds after this, the device reports another set of attributes which I believe are related to the device status. The value of this attribute during normal operation is 00200020, however it seems to have a new value: 00200030. You can see this report at No. 22279 and 22281.

I don't know if this is relevant or not, but there is also a lot of read attributes requests from another device to the twinguard about time reading, which the twinguard responds with "unsupported attribute". You can see one of these at No. 22880.

I am aware that I am throwing a lot of information at you, but it is difficult for me to filter out what is relevant and what is not since I am fairly new to both z2m and sniffing. Please let me know if and how I can help to make your life easier diagnosing the issue.

safakaltun avatar Dec 07 '22 07:12 safakaltun

Okay, I think I found the problem, but I have no idea how to fix it.

First, I noticed that when I pair the device next to the coordinator, it does not go into this strange fail mode. However, when I pair it in my office it does start beeping. So I have performed another sniff to see again how the pairing looks when the device is in my room.

Apparently, one of the first things the device does after pairing is asking for Time. You can see it in the attached sniff at No. 10608. However, the time it receives back is bogus. The router bulb sends back a time stamp that is in 100+ years in the future (UTC: Feb 7, 2136 07:28:15.000000000 Romance Standard Time). You can see this in No. 10633.

This puts the device in fail mode. I think this is simply done to ensure that the device is not used for more than it is safe to do so.

A0601_beeping_identified.zip

There are still some other things to figure out, but I think this is the first blocker placing the device in a fault mode. Do you have any suggestions on how to fix this in a robust way? I have placed a filter to filter out "Time" from the payloads from the router, but I don't know what/if this will affect any of the functionalities of the device. It seems to be working without beeping at the moment.

safakaltun avatar Dec 07 '22 15:12 safakaltun

I have placed a filter to filter out "Time" from the payloads from the router, but I don't know what/if this will affect any of the functionalities of the device.

How did you do this?

I think the best workaround for this is to enable joining only via the coordinator (can be done via the frontend) and pair it close to it.

Koenkk avatar Dec 07 '22 19:12 Koenkk