Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge πŸŒ‰, get rid of your proprietary Zigbee bridges πŸ”¨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
12.36k stars 1.69k forks source link

[New device support]: Linptech Presence Sensor ES1 (TS0225__TZ3218_awarhusb) #18637

Open aatifk opened 1 year ago

aatifk commented 1 year ago

Link

https://www.amazon.com/dp/B0C7C6L66J?ref=ppx_yo2ov_dt_b_product_details&th=1

Database entry

{"id":29,"type":"Router","ieeeAddr":"0xb43a31fffe265666","nwkAddr":29864,"manufId":4098,"manufName":"_TZ3218_awarhusb","powerSource":"Mains (single phase)","modelId":"TS0225","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":1026,"inClusterList":[0,3,4,5,57346,16384,61184,1280],"outClusterList":[10,25],"clusters":{"genBasic":{"attributes":{"modelId":"TS0225","manufacturerName":"_TZ3218_awarhusb","powerSource":1,"zclVersion":3,"appVersion":65,"stackVersion":1,"hwVersion":1,"dateCode":""}},"ssIasZone":{"attributes":{"iasCieAddr":"0x00124b0026b884c4","zoneState":0}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":65,"stackVersion":1,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1692243468779,"defaultSendRequestWhen":"immediate"}

Comments

I see another thread requesting support for TS0225 but the manufacturer and the product pages seem to be different for this one.

External converter

No response

Supported color modes

No response

Color temperature range

No response

kkossev commented 1 year ago

This device is rather different than the rest of the presence sensors based on the Tuya platform. It uses the standard ZCL cluster 0x0400 for illuminance reporting and the IAS cluster 0x0500 for occupancy reporting. It also uses a manufacturer-specific cluster 0xE002 for the configurable parameters and for reporting.

aatifk commented 1 year ago

ah, I see. I should probably just return this device, then.

kkossev commented 1 year ago

Amazon reviews are good, I would wait some more days or weeks, it will be surely supported in Z2M sooner or later ...

The form factor of this device is the same as the Xiaomi Mi-Home Bluetooth presence detector, and Xiaomi devices are typically of a much better quality than most of the similar things coming from AliExpress.

makeitworktech commented 1 year ago

It would be great to see this one work! Would this method still work to find the Tuya Data Points? https://www.zigbee2mqtt.io/advanced/support-new-devices/03_find_tuya_data_points.html

makeitworktech commented 1 year ago

Not sure if this information will help, but I can't get very far with an external converter.

Here's the illuminance cluster when my office light is off: 2023-09-06 17:37:35Received Zigbee message from '0xb43a31fffe265417', type 'raw', cluster 'msIlluminanceMeasurement', data '{"data":[8,90,10,0,0,33,1,0,0,0],"type":"Buffer"}' from endpoint 1 with groupID 0

Here's the illuminance cluster when my office light is turned on: 2023-09-06 17:43:17Received Zigbee message from '0xb43a31fffe265417', type 'raw', cluster 'msIlluminanceMeasurement', data '{"data":[8,89,10,0,0,33,134,111,0,0],"type":"Buffer"}' from endpoint 1 with groupID 0

And here is the manuSpecificTuya_2 cluster. It's at a distance and the value (shown as 214 below) goes down as i get closer to the device. Thinking distance? 2023-09-06 17:39:12Received Zigbee message from '0xb43a31fffe265417', type 'attributeReport', cluster 'manuSpecificTuya_2', data '{"57354":214}' from endpoint 1 with groupID 0

When I get really close to it, the cluster changes like this, the 57354 changing to 57345 and the 214 number jumping way up. It's like the distance measurement jumps to smaller units? 2023-09-06 17:50:48Received Zigbee message from '0xb43a31fffe265417', type 'attributeReport', cluster 'manuSpecificTuya_2', data '{"57345":4260}' from endpoint 1

And here's the IasZone cluster: 2023-09-06 17:43:49Received Zigbee message from '0xb43a31fffe265417', type 'commandStatusChangeNotification', cluster 'ssIasZone', data '{"extendedstatus":0,"zonestatus":1}' from endpoint 1 with groupID 0

I also get this genTime cluster, not sure what that does: 2023-09-06 17:44:17Received Zigbee message from '0xb43a31fffe265417', type 'read', cluster 'genTime', data '["localTime"]' from endpoint 1 with groupID 0

There's also a genBasic cluster that only shows up sometimes. 2023-09-06 17:54:55Received Zigbee message from '0xb43a31fffe265417', type 'attributeReport', cluster 'genBasic', data '{"65508":0,"appVersion":65}' from endpoint 1 with groupID 0

I just need to understand how these work rather than the typical Tuya "data points".

VikeDragon commented 1 year ago

Greetings! Acquired a rebranding from MOES with the same designation.

Koenkk commented 1 year ago

Let's start simple, does battery and occupancy work with this? https://gist.github.com/Koenkk/2e438754fac949869e5c2cf5219382aa

aatifk commented 1 year ago

I had returned this because I assumed it won't work. Let me try getting a hold of this again and try the external converter.

VikeDragon commented 1 year ago
  • ext_converter.js

Greetings, Koenkk! I checked this converter. For my sensor, everything works out - detects the presence. The sensor is not on batteries - it is on Type - With USB. Here are photos of the packaging and sensor, as well as the Myhome interface of the bluetooth analog Lamptech ES1, which I also have in stock. The settings of the analogue in MiHome are visible. I also attached analogs of sensor data files and logs.

main occupancy photo_2023-09-09_19-18-55 photo_2023-09-09_19-19-12 photo_2023-09-09_19-19-16 photo_2023-09-09_19-19-19 photo_2023-09-09_19-19-22 photo_2023-09-09_19-19-25 logs.log database.db.log state.json.log

makeitworktech commented 1 year ago
external_converters:
  - ext_converter.js

I've tried the external converter. Same as @VikeDragon, I get Occupancy, Battery, and Linkquality. Occupancy and Linkquality seem to work, but Battery is "Null" since it is USB powered.

image

kkossev commented 1 year ago

This device also exposes the illuminance via the standard ZCL cluster 0x0400

Koenkk commented 1 year ago

Added illuminance, removed battery: https://gist.github.com/Koenkk/2e438754fac949869e5c2cf5219382aa

makeitworktech commented 1 year ago

Added illuminance, removed battery: https://gist.github.com/Koenkk/2e438754fac949869e5c2cf5219382aa

Getting an error with the illuminance: Error 2023-09-11 19:19:38Failed to configure '0xb43a31fffe265417', attempt 3 (Error: ConfigureReporting 0xb43a31fffe265417/1 msIlluminanceMeasurement([{"attribute":"measuredValue","minimumReportInterval":10,"maximumReportInterval":3600,"reportableChange":5}], {"sendWhen":"immediate","timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"srcEndpoint":null,"reservedBits":0,"manufacturerCode":null,"transactionSequenceNumber":null,"writeUndiv":false}) failed (Timeout - 52598 - 1 - 6 - 1024 - 7 after 10000ms) at Timeout._onTimeout (/app/node_modules/zigbee-herdsman/src/utils/waitress.ts:64:35) at listOnTimeout (node:internal/timers:559:17) at processTimers (node:internal/timers:502:7))

image

kkossev commented 1 year ago

Try just binding the illuminance cluster without configuring the reporting. Also, send the Tuya BlackMagic packet during the pairing, although I don't know if it has any effect or not.

This is a rather weird device... Not typical Tuya data points implementation. It uses cluster 0xE002 for configuring the parameters: Cluster 0xE002, attribute 0xE00B - maximum detection distance, min:75, max:600, step:75, rw Cluster 0xE002, attribute 0xE004 - 'Large motion detection sensitivity', range 0 to 5, rw Cluster 0xE002, attribute 0xE005 - 'Small motion detection sensitivity', range 0 to 5, rw Cluster 0xE002, attribute 0xE00A - the measured distance to the moving object, ro Cluster 0xE002, attribute 0xE001 - 'existence time' (tuyaVersion 1.0.1 - in seconds, tuyaVersion 1.0.5 - in minutes), ro

Tuya cluster 0xEF00, data point 101 - the 'fading time', range 10..10000, rw

iculookn commented 1 year ago

Added illuminance, removed battery: https://gist.github.com/Koenkk/2e438754fac949869e5c2cf5219382aa

I also just got the MOES Human Presence Sensor and tried this ext converter and Lux is NULL

image

FYI - This is the correct image for the MOES Plug https://moeshouse.com/cdn/shop/products/moes-zigbee-human-presence-sensor-detector-radar-wave-detection-sensor-for-home-security-596265.png?v=1692258238&width=600

kkossev commented 1 year ago

Does the MOES sensor have exactly the same fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']) as the Linptech?

iculookn commented 1 year ago

Does the MOES sensor have exactly the same fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']) as the Linptech?

Yes looks like it

image

makeitworktech commented 1 year ago

Try just binding the illuminance cluster without configuring the reporting. Also, send the Tuya BlackMagic packet during the pairing, although I don't know if it has any effect or not.

Still showing Illuminance as Null, but, the error has gone away. Sending configure: tuya.configureMagicPacket didn't seem to make a difference either unfortunately.

image

I've sent an email to Linptech to see if they can help out.

xinsdr commented 1 year ago

Does the MOES sensor have exactly the same fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']) as the Linptech?

Yes looks like it

image

Same thing with MOES sensor.

iculookn commented 1 year ago

Does the MOES sensor have exactly the same fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']) as the Linptech?

Yes looks like it

image

Same thing with MOES sensor.

This is a screencap for the MOES version

iculookn commented 1 year ago

with

The MOES model number is:

ZSS-LP-HP02 CB03

image

alexvaltchev commented 1 year ago

Any update on the the Moes wwmave human presence sensor? Mine also shows as _TZ3218_awarhusb TS0225. What is the latest and best so far converter that exist? thanks

Screenshot_2

alexvaltchev commented 1 year ago

Screenshot_2023-09-14-15-42-37-690_com tuya smartlife Screenshot_2023-09-14-15-42-40-608_com tuya smartlife Screenshot_2023-09-14-15-42-30-903_com tuya smartlife Screenshot_2023-09-14-15-42-35-256_com tuya smartlife

Here are some screenshots from the Smart Home app. It is pretty basic sensor. IT shows only 4 variables.

  1. Motion sensitivity - 1 to 5
  2. MotionLESS sensitivity - 1 to 5
  3. Distance 75 cm to 600 cm (6 meters)
  4. Nobody time (10 sec to 10000 sec) - not sure what this is ? Probably like "fade time"

This is everything that can be changed, only 4 settings.

It shows the following:

  1. Presence/no presence
  2. Presence time (in minutes)
  3. Current distance (in cm)
  4. Illumination value (in lux)
  5. Presnece record.
  6. Illumination record.
joohann commented 1 year ago

IMG_8185 and IMG_8186

Some inside details πŸ˜‰

makeitworktech commented 1 year ago

Some inside details πŸ˜‰

Wow, so many of these devices have that same sensor inside! I'm testing a bunch of these and I'm finding that! Hopefully we can get this one working with Z2M as well!

PXL_20230915_113536308.jpg

alexvaltchev commented 1 year ago

Anything new on the subject? How can we use this mmwave sensor with Zigbee2MQTT?

makeitworktech commented 1 year ago

Still not able to figure out these messages: Debug 2023-09-18 19:56:17No converter available for 'ZG-205ZL' with cluster 'manuSpecificTuya_2' and type 'attributeReport' and data '{"57354":287}' Debug 2023-09-18 19:56:19No converter available for 'ZG-205ZL' with cluster 'manuSpecificTuya_2' and type 'attributeReport' and data '{"57354":121}'

I've tried adding fzLocal and tzLocal to the converter file to help describe the messages, which I believe is a target distance. Here's my 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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result: KeyValue = {};
            if (msg.data.hasOwnProperty('57354')) {
                result.target_distance = msg.data['57354'];
            }
            return result;
        },
    } as Fz.Converter,
};

const tzLocal = {
    target_distance: {
        key: ['target_distance'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'target_distance': {
                utils.assertString(value, 'target_distance');
                await entity.write('manuSpecificTuya_2', {'57354': {value});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    } as Tz.Converter,

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    fromZigbee: [fz.ias_occupancy_alarm_1, fz.illuminance, fzLocal.target_distance],
    toZigbee: [tzLocal.target_distance],
    configure: tuya.configureMagicPacket,
    exposes: [e.occupancy(), e.illuminance_lux(), e.target_distance()],
    // configure: async (device, coordinatorEndpoint, logger) => {
    //     const endpoint = device.getEndpoint(1);
    //     await reporting.bind(endpoint, coordinatorEndpoint, ['msIlluminanceMeasurement']);
    //     await reporting.illuminance(endpoint);
    // },
};

module.exports = definition;

But I'm getting an error: [19:58:41] INFO: Preparing to start... [19:58:42] INFO: Socat not enabled [19:58:43] INFO: Starting Zigbee2MQTT... /app/data/extension/externally-loaded.js:20 const result: KeyValue = {}; ^^^^^^ SyntaxError: Missing initializer in const declaration at new Script (node:vm:100:7) at createScript (node:vm:265:10) at Object.runInNewContext (node:vm:306:10) at loadModuleFromText (/app/lib/util/utils.ts:152:8) at loadModuleFromFile (/app/lib/util/utils.ts:159:12) at Object.getExternalConvertersDefinitions (/app/lib/util/utils.ts:169:25) at getExternalConvertersDefinitions.next (<anonymous>) at new ExternalConverters (/app/lib/extension/externalConverters.ts:12:20) at new Controller (/app/lib/controller.ts:84:58) at start (/app/index.js:106:18)

I just don't know enough about this stuff to do much more. Any help would be appreciated.

makeitworktech commented 1 year ago

Ok, I'm getting somewhere! I have the target distance working (sort of).

image

Here's my latest 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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        // type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57354')) {
                result['target_distance'] = msg.data['57354'];
            }
            // if (msg.data.hasOwnProperty('57345')) {
            //     result['target_distance'] = msg.data['57345'];
            // }
            return result;
        },
    },
};

const tzLocal = {
    target_distance: {
        key: ['target_distance'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'target_distance': {
                utils.assertString(value, 'target_distance');
                await entity.write('manuSpecificTuya_2', {'57354': {value}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
};    

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    // fromZigbee: [fz.ias_occupancy_alarm_1, fz.illuminance, fzLocal.target_distance],
    fromZigbee: [fz.ias_occupancy_alarm_1, fz.illuminance, fzLocal.target_distance],
    toZigbee: [tzLocal.target_distance, tzLocal.target_distance],
    configure: tuya.configureMagicPacket,
    exposes: [e.occupancy(),
                e.illuminance_lux(),
                e.numeric('target_distance', ea.STATE).withDescription('Distance to target').withUnit('cm'),
                e.numeric('radar_sensitivity',ea.STATE_SET).withValueMin(1).withValueMax(5).withValueStep(1)
                    .withDescription('sensitivity of the radar')],

    // configure: async (device, coordinatorEndpoint, logger) => {
    //     const endpoint = device.getEndpoint(1);
    //     await reporting.bind(endpoint, coordinatorEndpoint, ['msIlluminanceMeasurement']);
    //     await reporting.illuminance(endpoint);
    // },
};

module.exports = definition;

My biggest issue right now is figuring out the difference between these two messages:

Debug 2023-09-06 17:32:52No converter available for 'TS0225' with cluster 'manuSpecificTuya_2' and type 'attributeReport' and data '{"57345":3180}'

Debug 2023-09-06 17:32:54No converter available for 'TS0225' with cluster 'manuSpecificTuya_2' and type 'attributeReport' and data '{"57354":221}'
makeitworktech commented 1 year ago

It uses the standard ZCL cluster 0x0400 for illuminance reporting and the IAS cluster 0x0500 for occupancy reporting. It also uses a manufacturer-specific cluster 0xE002 for the configurable parameters and for reporting.

So this comment has prompted me to read up on ZCL clusters. I'm literally over my head here, but it looks like the standard Illuminance Measurement cluster (0x0400) has only 5 attributes, while the one reported here seems to have 10:

Debug 2023-09-19 06:22:36No converter available for 'TS0225' with cluster 'msIlluminanceMeasurement' and type 'raw' and data '{"data":[8,59,10,0,0,33,134,23,0,0],"type":"Buffer"}'

Does this indicate that the cluster is non-standard, so we kinda have to guess at what the attributes are? If we number the attribute positions in the array like this: [1,2,3,4,5,6,7,8,9,10], then positions 1,3,4,5,6,9,10 don't seem to change when I change the light level.

Soon @blakadder may have some input :-D

Koenkk commented 1 year ago

Does this indicate that the cluster is non-standard, so we kinda have to guess at what the attributes are?

Yes πŸ˜„

makeitworktech commented 1 year ago

Ok, I've gotten a little further based on work by @kkossev for Hubitat, but now I'm stuck! https://github.com/kkossev/Hubitat/blob/main/Drivers/Tuya%20Multi%20Sensor%204%20In%201/Tuya%20Multi%20Sensor%204%20In%201.groovy

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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57354')) {
                result['target_distance'] = msg.data['57354'];
            }
            return result;
        },
    },
    motion_detection_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57355')) {
                result['motion_detection_distance'] = msg.data['57355'];
            }
            return result;
        },
    },
    motion_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57348')) {
                result['motion_detection_sensitivity'] = msg.data['57348'];
            }
            return result;
        },
    },
    static_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57349')) {
                result['static_detection_sensitivity'] = msg.data['57349'];
            }
            return result;
        },
    },
};

const tzLocal = {
    target_distance: {
        key: ['target_distance'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'target_distance': {
                utils.assertNumber(value, 'target_distance');
                await entity.write('manuSpecificTuya_2', {'57354': {value}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_distance': {
                utils.assertNumber(value, 'motion_detection_distance');
                await entity.write('manuSpecificTuya_2', {'57355': {value}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    motion_detection_sensitivity: {
        key: ['motion_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_sensitivity': {
                utils.assertNumber(value, 'motion_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {'57348': {value}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    static_detection_sensitivity: {
        key: ['static_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'static_detection_sensitivity': {
                utils.assertNumber(value, 'static_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {'57349': {value}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
};    

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    fromZigbee: [fz.ias_occupancy_alarm_1, fzLocal.target_distance,fzLocal.motion_detection_distance,fzLocal.motion_detection_sensitivity,fzLocal.static_detection_sensitivity],
    toZigbee: [tzLocal.target_distance,tzLocal.motion_detection_distance,tzLocal.motion_detection_sensitivity,tzLocal.static_detection_sensitivity],
    configure: tuya.configureMagicPacket,
    exposes: [
        e.occupancy().withDescription('Presence state'),
        e.illuminance_lux(),
        e.numeric('target_distance', ea.STATE).withDescription('Distance to target').withUnit('cm'),
        e.numeric('motion_detection_distance',ea.STATE_SET).withValueMin(75).withValueMax(600).withValueStep(75).withDescription('Motion detection distance').withUnit('m'),
        e.numeric('keep_time',ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Presence keep time'),
        e.numeric('motion_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Motion detection sensitivity'),
        e.numeric('static_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Static detection sensitivity'),
        e.numeric('leave_time', ea.STATE).withValueMin(0).withValueMax(9999).withValueStep(1).withUnit('min').withDescription('Shows the duration of the absence in minutes')

    ],

    meta: {
        tuyaDatapoints: [
            [1, 'presence', tuya.valueConverter.trueFalse1],
            [4, 'motion_detection_distance', tuya.valueConverter.raw],
            [12, 'keep_time', tuya.valueConverter.raw],
            [15, 'motion_detection_sensitivity', tuya.valueConverter.raw],
            [16, 'static_detection_sensitivity', tuya.valueConverter.raw],
            [19, 'target_distance', tuya.valueConverter.raw],
            [20, 'illuminance_lux', tuya.valueConverter.raw],
            [101, 'leave_time', tuya.valueConverter.raw],
        ],
    },
};

module.exports = definition;

When trying to adjust "motion_detection_sensitivity", I got this error:

2023-09-22 17:32:19Publish 'set' 'motion_detection_sensitivity' to 'Linptech ES1 Presence Sensor' failed: 'Error: Write 0xb43a31fffe265417/1 manuSpecificTuya_2({"57348":{"value":2}}, {"sendWhen":"immediate","timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"srcEndpoint":null,"reservedBits":0,"manufacturerCode":null,"transactionSequenceNumber":null,"writeUndiv":false}) failed (Write for 'BUFFER' not available)'

Not sure what that means, but I was not able to figure out how to send data to the manuSpecificTuya_2 cluster. I think I've reached my limit here, so I'm going to wait on others to help. Linptech really couldn't give much technical help or documentation - they're just rebranding I think.

Koenkk commented 1 year ago

@makeitworktech we need to know the data type of motion_detection_sensitivity, can you provide the herdsman debug log when the device reports this?

See https://www.zigbee2mqtt.io/guide/usage/debug.html on how to enable the herdsman debug logging. Note that this is only logged to STDOUT and not to log files.

kkossev commented 1 year ago

Data type that worked on the other platform is 0x20 ( UINT8 )

Koenkk commented 1 year ago

Then try:

await entity.write('manuSpecificTuya_2', {57348 {value, type: 0x20}});
makeitworktech commented 1 year ago

Then try:

await entity.write('manuSpecificTuya_2', {57348 {value, type: 0x20}});

@Koenkk This worked great!

    motion_detection_sensitivity: {
        key: ['motion_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_sensitivity': {
                utils.assertNumber(value, 'motion_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57348: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },

I now have motion and static detection sensitivity working (I think). I will test to see if they have an effect, but they are not giving an error. I tried to adjust motion_detection_distance, but it gave me a range error, like got "300", range only allowed 0 to 255. That was with this config:

    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            let payload = null;
            switch (key) {
            case 'motion_detection_distance': {
                utils.assertNumber(value, 'motion_detection_distance');
                await entity.write('manuSpecificTuya_2', {57355: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },

So I tried to map values like below, but it only writes a '0' for motion_detection_distance. I don't get an error, just always a 0. Any tips on mapping number ranges?

    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            let payload = null;
            switch (key) {
            case 'motion_detection_distance': {
                payload = utils.mapNumberRange(utils.toNumber(value, 'motion_detection_distance'), 0, 255, 75, 600);
                await entity.write('manuSpecificTuya_2', {57355: {payload, type: 0x21}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
kkossev commented 1 year ago

@makeitworktech try changing the data type for the motion_detection_distance to 0x21 (UINT16) or 0x23 (UINT32).

makeitworktech commented 1 year ago

@makeitworktech try changing the data type for the motion_detection_distance to 0x21 (UINT16) or 0x23 (UINT32).

@kkossev 0x21 works, but the value of motion_detection_distance stays at 0 and never updates. 0x23 gives a data type error. It seems like the data type 0x21 is correct, but the mapping of 0 thru 255 to 75 to 600 is not functioning properly. A syntax issue is my guess?

makeitworktech commented 1 year ago

Got it! Confirmed motion_detection_distance works! Below is the converter I have so far. I'm trying to get msIlluminanceMeasurement next.

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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    // illuminance: {
    //     cluster: 'msIlluminanceMeasurement',
    //     type: ['raw'],
    //     convert: (model, msg, publish, options, meta) => {
    //         const result = {};
    //         if (msg.data.hasOwnProperty('data')) {
    //             result['illuminance'] = msg.data['data'];
    //         }
    //         return result;
    //     },
    // },
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57354')) {
                result['target_distance'] = msg.data['57354'];
            }
            return result;
        },
    },
    motion_detection_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57355')) {
                result['motion_detection_distance'] = msg.data['57355'];
            }
            return result;
        },
    },
    motion_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57348')) {
                result['motion_detection_sensitivity'] = msg.data['57348'];
            }
            return result;
        },
    },
    static_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57349')) {
                result['static_detection_sensitivity'] = msg.data['57349'];
            }
            return result;
        },
    },
};

const tzLocal = {
    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            let newDist = null;
            switch (key) {
            case 'motion_detection_distance': {
                utils.assertNumber(value, 'motion_detection_distance');
                await entity.write('manuSpecificTuya_2', {57355: {value, type: 0x21}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    motion_detection_sensitivity: {
        key: ['motion_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_sensitivity': {
                utils.assertNumber(value, 'motion_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57348: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    static_detection_sensitivity: {
        key: ['static_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'static_detection_sensitivity': {
                utils.assertNumber(value, 'static_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57349: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
};    

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    fromZigbee: [fz.ias_occupancy_alarm_1, fzLocal.target_distance,fzLocal.motion_detection_distance,fzLocal.motion_detection_sensitivity,fzLocal.static_detection_sensitivity,fz.illuminance],
    toZigbee: [tzLocal.motion_detection_distance,tzLocal.motion_detection_sensitivity,tzLocal.static_detection_sensitivity],
    configure: tuya.configureMagicPacket,
    exposes: [
        e.occupancy().withDescription('Presence state'),
        e.illuminance(),
        e.numeric('target_distance', ea.STATE).withDescription('Distance to target').withUnit('cm'),
        e.numeric('motion_detection_distance',ea.STATE_SET).withValueMin(75).withValueMax(600).withValueStep(75).withDescription('Motion detection distance').withUnit('cm'),
        e.numeric('keep_time',ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Presence keep time'),
        e.numeric('motion_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Motion detection sensitivity'),
        e.numeric('static_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Static detection sensitivity'),
        e.numeric('leave_time', ea.STATE).withValueMin(0).withValueMax(9999).withValueStep(1).withUnit('min').withDescription('Shows the duration of the absence in minutes')

    ],

    meta: {
        tuyaDatapoints: [
            [1, 'presence', tuya.valueConverter.trueFalse1],
            [4, 'motion_detection_distance', tuya.valueConverter.raw],
            [12, 'keep_time', tuya.valueConverter.raw],
            [15, 'motion_detection_sensitivity', tuya.valueConverter.raw],
            [16, 'static_detection_sensitivity', tuya.valueConverter.raw],
            [19, 'target_distance', tuya.valueConverter.raw],
            [20, 'illuminance_lux', tuya.valueConverter.raw],
            [101, 'leave_time', tuya.valueConverter.raw],
        ],
    },
};

module.exports = definition;
makeitworktech commented 1 year ago

I have everything working now except "Leave time"! image

Setting up illuminance is pretty janky, but here was my method. I'm ignoring all of the values in the illuminance buffer object except for position 7. For reference, here are the position numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]. In position 7 there is a number that increases with more light, and decreases with less light. I used a handheld lux meter shown below to record actual lux measurements vs the number that shows up in position 7. image https://www.amazon.com/gp/product/B075DC6X25/

I plotted the corresponding values, and got an exponential curve shown below. I used that equation to convert the values in position 7 to "actual Lux values. image

Anyway, here's my converter so far. If I shouldn't keep posting every time I make progress, let me know and I'll reduce how often I post 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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    illuminance: {
        cluster: 'msIlluminanceMeasurement',
        type: 'raw',
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            const buffer = msg.data;
            result['illuminance'] = Math.round(0.0001 * Math.pow(Number(buffer[7]), 3.413));
            return result;
        },
    },
// below is good
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57354')) {
                result['target_distance'] = msg.data['57354'];
            }
            return result;
        },
    },
    motion_detection_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57355')) {
                result['motion_detection_distance'] = msg.data['57355'];
            }
            return result;
        },
    },
    motion_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57348')) {
                result['motion_detection_sensitivity'] = msg.data['57348'];
            }
            return result;
        },
    },
    static_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57349')) {
                result['static_detection_sensitivity'] = msg.data['57349'];
            }
            return result;
        },
    },
    // Presence Keep Time: Updates every 40 seconds
    presence_keep_time: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57345')) {
                result['presence_keep_time'] = msg.data['57345'];
            }
            return result;
        },
    },
};

const tzLocal = {
    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            let newDist = null;
            switch (key) {
            case 'motion_detection_distance': {
                utils.assertNumber(value, 'motion_detection_distance');
                await entity.write('manuSpecificTuya_2', {57355: {value, type: 0x21}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    motion_detection_sensitivity: {
        key: ['motion_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_sensitivity': {
                utils.assertNumber(value, 'motion_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57348: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    static_detection_sensitivity: {
        key: ['static_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'static_detection_sensitivity': {
                utils.assertNumber(value, 'static_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57349: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
};    

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    fromZigbee: [fz.ias_occupancy_alarm_1, fzLocal.target_distance,fzLocal.motion_detection_distance,fzLocal.motion_detection_sensitivity,fzLocal.static_detection_sensitivity,fzLocal.illuminance,fzLocal.presence_keep_time],
    toZigbee: [tzLocal.motion_detection_distance,tzLocal.motion_detection_sensitivity,tzLocal.static_detection_sensitivity],
    configure: tuya.configureMagicPacket,
    exposes: [
        e.occupancy().withDescription('Presence state'),
        e.numeric('illuminance', ea.STATE).withDescription('Illuminance in lux').withUnit('lx'),
        e.numeric('target_distance', ea.STATE).withDescription('Distance to target').withUnit('cm'),
        e.numeric('motion_detection_distance',ea.STATE_SET).withValueMin(75).withValueMax(600).withValueStep(75).withDescription('Motion detection distance').withUnit('cm'),
        e.numeric('presence_keep_time',ea.STATE).withDescription('Presence keep time').withUnit('s'), 
        e.numeric('motion_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Motion detection sensitivity'),
        e.numeric('static_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Static detection sensitivity'),
        e.numeric('leave_time', ea.STATE).withValueMin(0).withValueMax(9999).withValueStep(1).withUnit('min').withDescription('Shows the duration of the absence in minutes')

    ],

    meta: {
        tuyaDatapoints: [
            [1, 'presence', tuya.valueConverter.trueFalse1],
            [4, 'motion_detection_distance', tuya.valueConverter.raw],
            [12, 'keep_time', tuya.valueConverter.raw],
            [15, 'motion_detection_sensitivity', tuya.valueConverter.raw],
            [16, 'static_detection_sensitivity', tuya.valueConverter.raw],
            [19, 'target_distance', tuya.valueConverter.raw],
            [20, 'localilluminance', tuya.valueConverter.raw],
            [101, 'leave_time', tuya.valueConverter.raw],
        ],
    },
};

module.exports = definition;
iculookn commented 1 year ago

Thanks for the great work. Keep posting your updates. I am testing them on the MOES version, and all working apart from the leave time.

I tried adding mine to my MOES Tuya hub but it was not working. After speaking to MOES support, they stated my Hub was too old to support and too old to do an update (2 years old), so I am waiting for a new Hub to arrive to test as well.

image

makeitworktech commented 1 year ago

I haven't been able to see any messages from the device that relate to "Leave time" or "fading time", so I'm not really sure how to proceed. The target distance is decently accurate (from some of my tests so far), and setting the motion detection distance does seem to work.

kkossev commented 1 year ago

I hope that next week my Moes sensor will arrive and I will be able to sniff the command sent by Smart Life app.

walberjunior commented 1 year ago

I don't know if you know or if this information helps anything, but the mmw sensor is the LD2410B from Hi-Link.

https://community.hubitat.com/uploads/default/original/3X/7/0/70bae0d40919d82c9b6e6434163a69bf31851995.jpeg

https://www.hlktech.net/index.php?id=988

https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH

mmmbobby commented 1 year ago

Anyone have an issue where the occupancy sensor gets stuck in ON state and never clears? I’m connected to z2m and have re-paired it a few times but always stays in occupied state I am running the latest converter code I have tried adjusting sensitivities, but still stays ON

@makeitworktech Could my sensor be defective or do you have any ideas I could try to get the occupancy working?

kkossev commented 1 year ago

@makeitworktech - the fadingTime ('nobody time') is configured via Tuya DP 101

You can remove in the convertor all the tuyaDatapoints except dp 101 and probably rename it to 'Fading Time' (or whatever other label is already in use in Z2M) - this is the delay in seconds, after which the device will send a no-presence report via the IAS cluster.

The 'leave_time' (the duration of the absence in minutes) is not reported for this type of radar.

makeitworktech commented 1 year ago

@kkossev , ok thanks, I've updated the converter. I have to wait until my kids are asleep so there's no presence and I can test the time. @alexvaltchev mentioned that the Tuya app shows 10-10000sec for this one. 10000 sec is 2.8 hours, seems extreme! I put mine at 10 sec and I'll test it out there. Here's the latest:

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 legacy = require('zigbee-herdsman-converters/lib/legacy');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const {KeyValue, Definition, Tz, Fz, Expose, KeyValueAny, KeyValueNumberString, KeyValueString} = require('zigbee-herdsman-converters/lib/types');

const fzLocal = {
    illuminance: {
        cluster: 'msIlluminanceMeasurement',
        type: 'raw',
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            const buffer = msg.data;
            //result['illuminance'] = Number(buffer[8]) * 4.7419 * Math.exp(0.0522);
            result['illuminance'] = Math.round(0.0001 * Math.pow(Number(buffer[7]), 3.413));
            return result;
        },
    },
// below is good
    target_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57354')) {
                result['target_distance'] = msg.data['57354'];
            }
            return result;
        },
    },
    motion_detection_distance: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57355')) {
                result['motion_detection_distance'] = msg.data['57355'];
            }
            return result;
        },
    },
    motion_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57348')) {
                result['motion_detection_sensitivity'] = msg.data['57348'];
            }
            return result;
        },
    },
    static_detection_sensitivity: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57349')) {
                result['static_detection_sensitivity'] = msg.data['57349'];
            }
            return result;
        },
    },
    // Presence Keep Time: Updates every 40 seconds
    presence_keep_time: {
        cluster: 'manuSpecificTuya_2',
        type: ['attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            if (msg.data.hasOwnProperty('57345')) {
                result['presence_keep_time'] = msg.data['57345'];
            }
            return result;
        },
    },
};

const tzLocal = {
    motion_detection_distance: {
        key: ['motion_detection_distance'],
        convertSet: async (entity, key, value, meta) => {
            let newDist = null;
            switch (key) {
            case 'motion_detection_distance': {
                utils.assertNumber(value, 'motion_detection_distance');
                await entity.write('manuSpecificTuya_2', {57355: {value, type: 0x21}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    motion_detection_sensitivity: {
        key: ['motion_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'motion_detection_sensitivity': {
                utils.assertNumber(value, 'motion_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57348: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
    static_detection_sensitivity: {
        key: ['static_detection_sensitivity'],
        convertSet: async (entity, key, value, meta) => {
            switch (key) {
            case 'static_detection_sensitivity': {
                utils.assertNumber(value, 'static_detection_sensitivity');
                await entity.write('manuSpecificTuya_2', {57349: {value, type: 0x20}});
                break;
            }
            default: // Unknown key
                meta.logger.warn(`Unhandled key ${key}`);
            }
        },
    },
};    

const definition = {
    fingerprint: tuya.fingerprint('TS0225', ['_TZ3218_awarhusb']),
    model: 'TS0225',
    vendor: 'TuYa',
    description: 'Presence sensor',
    fromZigbee: [fz.ias_occupancy_alarm_1, fzLocal.target_distance,fzLocal.motion_detection_distance,fzLocal.motion_detection_sensitivity,fzLocal.static_detection_sensitivity,fzLocal.illuminance,fzLocal.presence_keep_time, tuya.fz.datapoints],
    toZigbee: [tzLocal.motion_detection_distance,tzLocal.motion_detection_sensitivity,tzLocal.static_detection_sensitivity, tuya.tz.datapoints],
    configure: tuya.configureMagicPacket,
    exposes: [
        e.occupancy().withDescription('Presence state'),
        e.numeric('illuminance', ea.STATE).withDescription('Illuminance in lux').withUnit('lx'),
        e.numeric('target_distance', ea.STATE).withDescription('Distance to target').withUnit('cm'),
        e.numeric('motion_detection_distance',ea.STATE_SET).withValueMin(75).withValueMax(600).withValueStep(75).withDescription('Motion detection distance').withUnit('cm'),
        e.numeric('presence_keep_time',ea.STATE).withDescription('Presence keep time').withUnit('s'), 
        e.numeric('motion_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Motion detection sensitivity'),
        e.numeric('static_detection_sensitivity', ea.STATE_SET).withValueMin(0).withValueMax(5).withValueStep(1).withDescription('Static detection sensitivity'),
        e.numeric('fading_time', ea.STATE_SET).withValueMin(10).withValueMax(10000).withValueStep(1).withUnit('sec').withDescription('Time after which the device will check again for presence')

    ],

    meta: {
        tuyaDatapoints: [
            [101, 'fading_time', tuya.valueConverter.raw],
        ],
    },
};

module.exports = definition;
Scope666 commented 1 year ago

Just received the Linptech today, using your latest external .js ... looks great so far!

image
makeitworktech commented 1 year ago

Anyone have an issue where the occupancy sensor gets stuck in ON state and never clears?

I haven't had this issue. I'd say get a replacement and try that.

Scope666 commented 1 year ago

The unit is CRAZY sensitive. I had to set the motion and static sensitivities all the way down, and set the range to just what I need, which is around 300 cm. Try that first before returning. If it's set too high, ANYTHING can set it off. Also it needs to mounted in a SOLID way. If someone walks heavy in another room, that can cause a micro vibration that it will pick up if set too high.

image
mmmbobby commented 1 year ago

@makeitworktech @Scope666 thanks for the tips. I set the sensitivity all the way down and still the same result. It’s weird because the blue light comes on when I come in the room (indicating that the hardware seems to detect presence), but the sensor never changes. Also, the other sensors work fine (specifically target distance, so I know it detects me). I’ll try a few more things, and if nothing works, I’ll order a new one