Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.51k stars 1.63k forks source link

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

Closed safakaltun closed 1 year ago

safakaltun commented 1 year ago

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 commented 1 year ago

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

safakaltun commented 1 year ago

@Koenkk do you have any suggestions I can try?

Koenkk commented 1 year ago

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

safakaltun commented 1 year ago

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.

Koenkk commented 1 year ago

Try changing the configure to this:

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

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()],
};
Koenkk commented 1 year ago

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));
    },
safakaltun commented 1 year ago

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.

Koenkk commented 1 year ago

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

safakaltun commented 1 year ago

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

Koenkk commented 1 year ago

Is this device paired through an install code?

safakaltun commented 1 year ago

Yes

safakaltun commented 1 year ago

Here is the content of the install code just in case

RB01SG0D836591B3CC0010000000000000000000000D6F00179E01B9DLK6B0670B9EE705E9B3E3271D1B90B1A8F

Koenkk commented 1 year ago

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.

safakaltun commented 1 year ago

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

Koenkk commented 1 year ago

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.

safakaltun commented 1 year ago

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 commented 1 year ago

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

Koenkk commented 1 year ago

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.

safakaltun commented 1 year ago

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

image

Koenkk commented 1 year ago

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

safakaltun commented 1 year ago

Seems like it does receive a message with the same attributes

image

safakaltun commented 1 year ago

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 commented 1 year ago

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'),
        ],
};
Koenkk commented 1 year ago

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?

safakaltun commented 1 year ago

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

Koenkk commented 1 year ago

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.

safakaltun commented 1 year ago

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 commented 1 year ago

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.

Koenkk commented 1 year ago

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.

safakaltun commented 1 year ago

In the settings of the router I used the “ Filtered publish attributes” field and added Time. Unfortunately though, after a couple of hours device started beeping again. Since I have 2 twinguards, I am testing in parallel. The device that I paired and left next to the Coordinator is still working and measuring fine. The other device that I paired using the usual “Permit Join (All)” started beeping after a couple of hours. I don’t know why though as I stopped sniffing. Additionally, I doubt that pairing only through the Coordinator will work. During my tests, I found that if I move the device that I paired through the coordinator to somewhere else in the house, it starts beeping as well. I don’t know why…

Koenkk commented 1 year ago

In the settings of the router I used the “ Filtered publish attributes” field and added Time.

This only prevent the attribute being published to MQTT, it has no influence on the actual messages send by the device. Only Zigbee2MQTT is aware of this option (not even the coordinator).

safakaltun commented 1 year ago

OK, so that was a coincidence then. I am happy that is the case since I couldn’t make any sense of why it would start beeping again.

Is there any way to prevent the router sending its own time to the device instead of the coordinator time or is there a way to update the time of the router? This device is the router. The final solution should not be dependent on the router of course.

Koenkk commented 1 year ago

Is there any way to prevent the router sending its own time to the device instead of the coordinator time

No, this behavior is programmed in the firmware

is there a way to update the time of the router?

The device should request the time from the coordinator, but it doesn't seem to do that.

Awox is also not the best brand, I've already seen more strange behavior with these devices.

safakaltun commented 1 year ago

Awox is also not the best brand, I've already seen more strange behavior with these devices.

Yes, unfortunately. I also don’t understand why the other devices don’t use any of the ~50 philips hue bulbs available in the network. The Avox bulb is definitely not the most central router. I find it odd that most of my devices use that as a router.

Would changing the Awox bulb’s type from “router” to “end device” force the devices in my network to connect to an another router? If so, I can try that as a workaround just to progress. In the final product page, we can perhaps disclaim this as a compatibility issue.

Koenkk commented 1 year ago

Would changing the Awox bulb’s type from “router” to “end device” force the devices in my network to connect to an another router?

This is not possible, you cannot change device types within Zigbee (without modifying the software on the device itself)

safakaltun commented 1 year ago

Thanks.

I removed the Awox bulb from the network and the sirens has been working without any issues. Now, the next step for me is to understand how I can send commands to the device for both device specific settings (seems to be done using write attributes) and trigger actions such as testing or triggering the siren (done via Unknown Commands). I can follow what has been done for https://github.com/Koenkk/zigbee2mqtt/issues/13520 in https://github.com/Koenkk/zigbee-herdsman/pull/623, but I am missing a fundamental information: I have no idea where to find the clusters.ts file in my home assistant setup. My z2m installation was done using the Add-Ons store, so I cannot find the library to update and recompile. Could you please help me with that?

Another issue I need help with is the cluster naming. The device receives commands from 3 clusters (0xe000, 0xe006 and 0xe007) that are not defined in the clusters.ts file. Is there a naming convention I need to follow naming these clusters in the clusters.ts file? Based on what is in the cluster.ts, I would use manuSpecificBosch, manuSpecificBosch2, manuSpecificBosch3, but I am not sure if that is okay.

Lastly, can the same cluster ID be used to define multiple clusters? For example, currently 0xe004 is used for zosungIRControl, but I don't want to add commands to that since it is introduced for a different brand device with different commands. Can I define 0xe004 as manuSpecificBosch4 and add the relevant commands under it?

danieledwardgeorgehitchcock commented 1 year ago

@safakaltun I don't mind looking into the cluster.ts file for you as I did in the Bosch Outdoor Siren thread a few days back.

I can see that you have done some sniffs - let's try and break down the commands and development into smaller requirements.

Could you list the functionality from most important to least important that you want adding, and the relevant sniff / command that demonstrates the behaviour between the twinguard and the home controller?

safakaltun commented 1 year ago

@danieledwardgeorgehitchcock I, really, really appreciate the offer, but I would like to do it myself, so that I can also independently contribute to the project without being a burden to the community in the long term. If you can point me to a resource on how/where to do the development instead, I would be even more grateful.

I would be happy if you can answer the following questions:

danieledwardgeorgehitchcock commented 1 year ago

That's very noble and is the same journey that I am on :-)

I am not too familiar with Z2M in a Home Assistant environment - If I recall, it is just using the HA Supervisor to spin up a Z2M docker container?

Can you access the filesystem of the Z2M container in HA? If so, then you can replicate what I did for the PR on the Bosch Outdoor Siren.

If you want to test using a live instance, you can modify your cluster.js file in the following directory using the config directory as a start point: ./node_modules/zigbee-herdsman/dist/zcl/definition/cluster.js this is a compiled version of the cluster.ts file found in the zigbee-herdsman project. You will have to resolve types and references yourself in the file - I couldn't get a reference to the datatypes definition to work so manually coded in the hex.

See how you get on, happy to help you where I can!

safakaltun commented 1 year ago

Well, thank you!

I actually managed to progress a bit today by installing z2m on my Windows. I am now able to send commands to the device to both initiate the self-test and trigger the siren. However, I am unable to send write commands. I mean, I can send the write commands, but the data is not correct. Below, you can find my converter:

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(16387)) {
//            result.airpurity = msg.data['0x4003'] * 10.0 + 500.0 ;
                result.airpurity = msg.data['16387'] * 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 ;
            }
            if (msg.data.hasOwnProperty(16390)) {
//            result.battery = msg.data['0x4006'] / 2.0 ;
                result.battery = msg.data['16390'] / 2.0 ;
            }
            if (msg.data.hasOwnProperty(16391)) {
                result.unknown1 = msg.data['16391'] ;
            }
            if (msg.data.hasOwnProperty(16392)) {
                result.unknown2 = msg.data['16392'] ;
            }
            if (msg.data.hasOwnProperty(16393)) {
                result.unknown3 = msg.data['16393'] ;
            }
            if (msg.data.hasOwnProperty(16394)) {
                result.unknown4 = msg.data['16394'] ;
            }
            if (msg.data.hasOwnProperty(16395)) {
                result.unknown5 = msg.data['16395'] ;
            }
            if (msg.data.hasOwnProperty(16396)) {
                result.unknown6 = msg.data['16396'] ;
            }
            if (msg.data.hasOwnProperty(16385)) {
                result.unknown7 = msg.data['16385'] ;
            }
            if (msg.data.hasOwnProperty(16386)) {
                result.unknown8 = msg.data['16386'] ;
            }
            return result;
        },
    },
};
const tzLocal = {
    Twinguard_smoke_sensitivity: {
        key: ['sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            const lookup = {'low': 0x0003, 'medium': 0x0002, 'high': 0x0001};
            await entity.write('manuSpecificBosch', {smokeSensitivity: {value: lookup[value], type: 0x21}},
                {manufacturerCode: 0x1209});
            return {state: {sensitivity: value}};
        },
    }
};
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: [tzLocal.Twinguard_smoke_sensitivity],
    configure: async (device, coordinatorEndpoint, logger) => {
        const coordinatorEndpointB = coordinatorEndpoint.getDevice().getEndpoint(1);
        await reporting.bind(device.getEndpoint(1), coordinatorEndpointB, [0x0009]);
        await reporting.bind(device.getEndpoint(7), coordinatorEndpointB, [0x0019]);
        await reporting.bind(device.getEndpoint(7), coordinatorEndpointB, [0x0020]);
        await reporting.bind(device.getEndpoint(1), coordinatorEndpointB, [0xe000]);
        await reporting.bind(device.getEndpoint(3), coordinatorEndpointB, [0xe002]);
        await reporting.bind(device.getEndpoint(1), coordinatorEndpointB, [0xe004]);
        await reporting.bind(device.getEndpoint(12), coordinatorEndpointB, [0xe006]);
        await reporting.bind(device.getEndpoint(12), coordinatorEndpointB, [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'),
        exposes.numeric('battery', ea.STATE).withUnit('%').withDescription('Remaining battery in %').withValueMin(0).withValueMax(100),
        exposes.numeric('unknown1', ea.STATE).withUnit('a'),
        exposes.numeric('unknown2', ea.STATE).withUnit('b'),
        exposes.numeric('unknown3', ea.STATE).withUnit('c'),
        exposes.numeric('unknown4', ea.STATE).withUnit('d'),
        exposes.numeric('unknown5', ea.STATE).withUnit('e'),
        exposes.numeric('unknown6', ea.STATE).withUnit('f'),
        exposes.numeric('unknown7', ea.STATE).withUnit('g'),
        exposes.numeric('unknown8', ea.STATE).withUnit('h'),
        exposes.enum('sensitivity', ea.STATE_SET, ['low', 'medium', 'high'])
                .withDescription('Sets the sensitivity of the smoke alarm.')
        ],
};

module.exports = definition;

And the following part is the addition I am making to the cluster.ts file

    manuSpecificBosch: {
        ID: 0xe000,
        manufacturerCode: ManufacturerCode.Bosch,
        attributes: {
            smokeSensitivity: {ID: 0x4003, type: 0x21}, // value: 1,2,3
        },
        commands: { /////////// VALIDATED  ////////////
            testSmokeAlarm: {
                ID: 0x00,
                parameters: [],
            },
        },
        commandsResponse: {
        },
    },  

So, the problem here is simply whatever I pick for the "sensitivity", the data in the payload is always 0 as shown in the snippet below.

image

Instead of 0, we would expect to see any value between 1 and 3. Any ideas what can be the problem?

danieledwardgeorgehitchcock commented 1 year ago

Should smokeSensitivity be a command rather than an attribute? what does your sniff look like for this with the Bosch controller? I am away from my Wireshark machine at the moment so cannot open your pcaps...

safakaltun commented 1 year ago

I don't think so. smokeSensitivity is just an attribute according to the sniff. Below you can see the sniff for the same command when I send the command from the Bosch App.

image

I think whatever the problem is, it is happening because I am not doing something right with the converter. :/

safakaltun commented 1 year ago

Hi, I have progressed quite a bit on this. I have added all the clusters that the device is using to the clusters.ts file. I have made a draft PR, and now I need your help. When the device reports attributes, it reports a lot of information that I cannot make sense of. In the clusters file I named these attributes as "unknown#" with the hope that someone in the community can make some sense of what they are and name them properly. Is this an acceptable approach or shall I just remove them? @Koenkk can you please advise? Below is the link to the draft PR.

https://github.com/Koenkk/zigbee-herdsman/pull/630

I have also updated the bosch.js file in the zigbee-herdsman-converters. The draft PR can be found below.

https://github.com/Koenkk/zigbee-herdsman-converters/pull/5176

The key point for the both PRs is the handling of the unknown attributes. If you can guide me about them, I can finalize this task this week.

PS. Funnily enough, I have not managed to look into what the device reports when it detects smoke yet. the final PR will include the smoke signal as well.

safakaltun commented 1 year ago

@danieledwardgeorgehitchcock I think, I am done with this device! Could you please pull the most recent z2m dev version and test it? Since the PR for the converter has not been approved yet, you might need to manually update the bosch.js to test it (using the file in https://github.com/Koenkk/zigbee-herdsman-converters/pull/5176). Let me know if you experience any issues. :)

dubtec commented 1 year ago

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

Hi @safakaltun ,

I have the same device (also with 8750001215) as you, but the interview fails for me: error 2023-01-20 16:15:19: Failed to interview '0x000d6f001627238a', device has not successfully been paired

Since You're referring to an install code, are there special instructions needed to pair this with zigbee2mqtt?

Also, in the devices database there's a Bosch Twinguard listed, but only with 8750001213.

May I kindly ask for Your assistance?

Best regards

UPDATE: After re-reading this thread and some further research, I found the "add install code" in Settings > Tools. After further research I found that it would need the whole QR code's string, which I could add. Now the device paired successfully. Hope this helps others stumbling over this.

safakaltun commented 1 year ago

Hi @dubtec, I see that you managed to pair the device. Does everything work fine? If so, I think I should close the task.

dubtec commented 1 year ago

Hi @safakaltun,

actually this was open before. I didn’t explicitly did so. And, unfortunately, I don’t see an option to close it. Is this not an exclusive privilege of the opening person?

If not, please instruct me, thx!

Best regards

safakaltun commented 1 year ago

I think I just forgot closing it after pushing the changes. So you did nothing wrong! :)

safakaltun commented 1 year ago

Closed with the following PRs: https://github.com/Koenkk/zigbee-herdsman/pull/630

https://github.com/Koenkk/zigbee-herdsman-converters/pull/5176