Koenkk / zigbee2mqtt

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

[New device support]: Aqara S3 Thermostat #15604

Closed masafra86 closed 1 year ago

masafra86 commented 1 year ago

Link

https://www.aqara.com/Smart-Thermostat-S3_overview

Database entry

{"id":29,"type":"Router","ieeeAddr":"0x54ef4410005e6e16","nwkAddr":33299,"manufId":4447,"manufName":"Aqara","powerSource":"DC Source","modelId":"lumi.airrtc.pcacn2","epList":[1,2,3],"endpoints":{"1":{"profId":260,"epId":1,"devId":769,"inClusterList":[1029,64704,3,0],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"dateCode":"20220225","modelId":"lumi.airrtc.pcacn2"}},"aqaraOpple":{"attributes":{"238":1248015,"247":{"type":"Buffer","data":[3,40,37,5,33,54,0,9,33,0,6,10,33,0,0,13,35,15,11,19,0,14,35,0,0,0,0,200,32,3,201,39,252,254,63,8,158,7,190,10,154,32,0]},"308":{"type":"Buffer","data":[148,52,105,229,130,242]},"527":{"type":"Buffer","data":[196,233,90,49,205,244,115,204,96,82,248,69,55,253,155,40]},"528":1,"529":24,"532":1,"533":0,"534":15,"535":3,"536":1,"545":1,"546":55,"551":1,"552":"911(68)","554":5,"572":1,"586":0,"590":3,"591":"0x0abe079e083ffefc","592":"0x0000000000000000","593":1,"594":0,"596":1,"597":80,"598":0,"599":352323840,"600":0,"601":1,"602":1,"605":1,"606":{"type":"Buffer","data":[]}}},"msRelativeHumidity":{"attributes":{"measuredValue":6100}}},"binds":[],"configuredReportings":[],"meta":{}},"2":{"profId":260,"epId":2,"devId":769,"inClusterList":[64704],"outClusterList":[],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}},"3":{"profId":260,"epId":3,"devId":769,"inClusterList":[64704],"outClusterList":[],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":15,"stackVersion":27,"hwVersion":1,"dateCode":"20220225","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1671271532341,"defaultSendRequestWhen":"immediate"}

Comments

Sorry I made a mistake copying the database in the previous issue: the right device is [lumi.airrtc.pcacn2] as stated in the external converter I tried to create, and the comment in the previous issue was:

I'm following all the steps to add this thermostat, but I'm not able to get past the pairing step, after pairing it, creating the .js file and adding it to the external converters entry in the configuration, I still get the same error. I'm trying to modify another file about a similar device i found, that is this one:

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

const manufacturerOptions = { xiaomi: {manufacturerCode: 0x115f, disableDefaultResponse: true}, };

const tzLocal = { ZNCJMB14LM: { key: ['theme' ], convertSet: async (entity, key, value, meta) => { if (key === 'theme') { const lookup = {'classic': 0, 'concise': 1}; await entity.write('aqaraOpple', {0x0215: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {theme: value}}; } else { throw new Error(Not supported: '${key}'); } }, }, };

const fzLocal = { ZNCJMB14LM: { cluster: 'aqaraOpple', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.hasOwnProperty(0x0215)) { const lookup = {0: 'classic', 1: 'concise'}; result.theme = lookup[msg.data[0x0215]]; } return result; }, }, };

const definition = { zigbeeModel: ['lumi.switch.n4acn4'], model: 'ZNCJMB14LM', vendor: 'Xiaomi', description: 'Aqara S1 Smart Touch Panel', fromZigbee: [fz.on_off, fzLocal.ZNCJMB14LM], toZigbee: [tz.on_off, tzLocal.ZNCJMB14LM], meta: {multiEndpoint: true}, endpoint: (device) => { return {'l1': 1, 'l2': 2, 'l3': 3}; }, exposes: [ e.switch() .withEndpoint('l1') .setAccess('state', ea.STATE_SET), e.switch() .withEndpoint('l2') .setAccess('state', ea.STATE_SET), e.switch() .withEndpoint('l3') .setAccess('state', ea.STATE_SET), exposes.enum('theme', ea.SET, ['classic', 'concise']) .withDescription('Display theme'), ], configure: async (device, coordinatorEndpoint, logger) => { await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']); await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']); await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff']); await reporting.onOff(device.getEndpoint(1)); }, ota: ota.zigbeeOTA, };

module.exports = definition;

Yesterday I work on it and I managed to have some of the display things working, such as LCD brightness, sounds and language selection plus Humidity and device temperature. I'm still not able to move the switches and get the temperature, and some sort of havoc control

External converter

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const herdsman = require('zigbee-herdsman');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const ota = require('zigbee-herdsman-converters/lib/ota');
const e = exposes.presets;
const ea = exposes.access;

const manufacturerOptions = {

    xiaomi: {manufacturerCode: herdsman.Zcl.ManufacturerCode.LUMI_UNITED_TECH, disableDefaultResponse: true},

};

const fzLocal = {
    ZNWKQ11LM: {
        cluster: 'aqaraOpple',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
//            if (msg.data.hasOwnProperty(0x0215)) {
//                const lookup = {0: 'classic', 1: 'concise'};
//                result.theme = lookup[msg.data[0x0215]];
//            }
            if (msg.data.hasOwnProperty(0x0214)) {
                const lookup = {1: 'pointer clock', 2: 'world photos', 3: 'time of the day'};
                result.screen_saver_style = lookup[msg.data[0x0214]];
            }
            if (msg.data.hasOwnProperty(0x0213)) {
                result.standby_enabled = msg.data[0x0213] & 1 ? true : false;
            }
            if (msg.data.hasOwnProperty(0x0254)) {
                const lookup = {0: 'mute', 1: 'high'};
                result.beep_volume = lookup[msg.data[0x0254]];
            }
            if (msg.data.hasOwnProperty(0x0255)) {
                result.lcd_brightness = msg.data[0x0255];
            }
            if (msg.data.hasOwnProperty(0x022b)) {
                const lookup = {0: 'none', 1: '1', 2: '2', 3: '1 and 2', 4: '3', 5: '1 and 3', 6: '2 and 3', 7: 'all'};
                result.available_switches = lookup[msg.data[0x022b]];
            }
            if (msg.data.hasOwnProperty(0x217)) {
                const lookup = {3: 'small', 4: 'medium', 5: 'large'};
                result.font_size = lookup[msg.data[0x217]];
            }
            if (msg.data.hasOwnProperty(0x219)) {
                const lookup = {0: 'scene', 1: 'feel', 2: 'thermostat', 3: 'switch'};
                result.homepage = lookup[msg.data[0x219]];
            }
            if (msg.data.hasOwnProperty(0x210)) {
                const lookup = {0: 'chinese', 1: 'english'};
                result.language = lookup[msg.data[0x210]];
            }
            if (msg.data.hasOwnProperty(0x216)) {
                result.standby_time = msg.data[0x216];
            }
            if (msg.data.hasOwnProperty(0x0218)) {
                result.lcd_auto_brightness_enabled = msg.data[0x0218] & 1 ? true : false;
            }
            if (msg.data.hasOwnProperty(0x221)) {
                result.screen_saver_enabled = msg.data[0x221] & 1 ? true : false;
            }
            if (msg.data.hasOwnProperty(0x0255)) {
                result.standby_lcd_brightness = msg.data[0x0255];
            }
            if (msg.data.hasOwnProperty(0x223)) {
                const lookup = {1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11'};
                const textarr = msg.data[0x223].slice(1, msg.data[0x223].length);
                result.switch_1_icon = lookup[msg.data[0x223][0]];
                result.switch_1_text = String.fromCharCode(...textarr);
            }
            if (msg.data.hasOwnProperty(0x224)) {
                const lookup = {1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11'};
                const textarr = msg.data[0x224].slice(1, msg.data[0x224].length);
                result.switch_2_icon = lookup[msg.data[0x224][0]];
                result.switch_2_text = String.fromCharCode(...textarr);
            }
            if (msg.data.hasOwnProperty(0x225)) {
                const lookup = {1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11'};
                const textarr = msg.data[0x225].slice(1, msg.data[0x225].length);
                result.switch_3_icon = lookup[msg.data[0x225][0]];
                result.switch_3_text = String.fromCharCode(...textarr);
            }
            return result;
        },
    },
};
const tzLocal = {

    ZNWKQ11LM: {
        key: ['standby_enabled',
            'beep_volume',
            'lcd_brightness',
            'language',
            'screen_saver_style',
            'standby_time',
            'font_size',
            'lcd_auto_brightness_enabled',
            'homepage',
            'screen_saver_enabled',
            'standby_lcd_brightness',
            'available_switches',
            'switch_1_text_icon',
            'switch_2_text_icon',
            'switch_3_text_icon',
        ],
        convertSet: async (entity, key, value, meta) => {
            if (key === 'standby_enabled') {
                await entity.write('aqaraOpple', {0x0213: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
                return {state: {standby_enabled: value}};
            } else if (key === 'beep_volume') {
                const lookup = {'mute': 0, 'high': 1};
                await entity.write('aqaraOpple', {0x0212: {value: lookup[value], type: 0x10}}, manufacturerOptions.xiaomi);
                return {state: {beep_volume: value}};
            } else if (key === 'lcd_brightness') {
                await entity.write('aqaraOpple', {0x0255: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {lcd_brightness: value}};
            } else if (key === 'language') {
                const lookup = {'chinese': 0, 'english': 1};
                await entity.write('aqaraOpple', {0x0210: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {language: value}};
            } else if (key === 'screen_saver_style') {
                const lookup = {'classic': 1, 'analog clock': 2};
                await entity.write('aqaraOpple', {0x0214: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {screen_saver_style: value}};
            } else if (key === 'standby_time') {
                await entity.write('aqaraOpple', {0x0216: {value: value, type: 0x23}}, manufacturerOptions.xiaomi);
                return {state: {standby_time: value}};
            } else if (key === 'font_size') {
                const lookup = {'small': 3, 'medium': 4, 'large': 5};
                await entity.write('aqaraOpple', {0x0217: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {font_size: value}};
            } else if (key === 'lcd_auto_brightness_enabled') {
                await entity.write('aqaraOpple', {0x0218: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
                return {state: {lcd_auto_brightness_enabled: value}};
            } else if (key === 'homepage') {
                const lookup = {'scene': 0, 'feel': 1, 'thermostat': 2, 'switch': 3};
                await entity.write('aqaraOpple', {0x0219: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {homepage: value}};
            } else if (key === 'screen_saver_enabled') {
                await entity.write('aqaraOpple', {0x0221: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
                return {state: {screen_saver_enabled: value}};
            } else if (key === 'standby_lcd_brightness') {
                await entity.write('aqaraOpple', {0x0222: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {standby_lcd_brightness: value}};
            } else if (key === 'available_switches') {
                const lookup = {'none': 0, '1': 1, '2': 2, '1 and 2': 3, '3': 4, '1 and 3': 5, '2 and 3': 6, 'all': 7};
                await entity.write('aqaraOpple', {0x022b: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
                return {state: {available_switches: value}};
            } else if (key === 'switch_1_text_icon') {
                const lookup = {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, '11': 11};
                const payload = [];
                const statearr = {};
                if (value.hasOwnProperty('switch_1_icon')) {
                    payload.push(lookup[value.switch_1_icon]);
                    statearr.switch_1_icon = value.switch_1_icon;
                } else {
                    payload.push(1);
                    statearr.switch_1_icon = '1';
                }
                if (value.hasOwnProperty('switch_1_text')) {
                    payload.push(...value.switch_1_text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_1_text = value.switch_1_text;
                } else {
                    payload.push(...''.text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_1_text = '';
                }
                await entity.write('aqaraOpple', {0x0223: {value: payload, type: 0x41}}, manufacturerOptions.xiaomi);
                return {state: statearr};
            } else if (key === 'switch_2_text_icon') {
                const lookup = {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, '11': 11};
                const payload = [];
                const statearr = {};
                if (value.hasOwnProperty('switch_2_icon')) {
                    payload.push(lookup[value.switch_2_icon]);
                    statearr.switch_2_icon = value.switch_2_icon;
                } else {
                    payload.push(1);
                    statearr.switch_2_icon = '1';
                }
                if (value.hasOwnProperty('switch_2_text')) {
                    payload.push(...value.switch_2_text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_2_text = value.switch_2_text;
                } else {
                    payload.push(...''.text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_2_text = '';
                }
                await entity.write('aqaraOpple', {0x0224: {value: payload, type: 0x41}}, manufacturerOptions.xiaomi);
                return {state: statearr};
            } else if (key === 'switch_3_text_icon') {
                const lookup = {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, '11': 11};
                const payload = [];
                const statearr = {};
                if (value.hasOwnProperty('switch_3_icon')) {
                    payload.push(lookup[value.switch_3_icon]);
                    statearr.switch_3_icon = value.switch_3_icon;
                } else {
                    payload.push(1);
                    statearr.switch_3_icon = '1';
                }
                if (value.hasOwnProperty('switch_3_text')) {
                    payload.push(...value.switch_3_text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_3_text = value.switch_3_text;
                } else {
                    payload.push(...''.text.split('').map((c) => c.charCodeAt(0)));
                    statearr.switch_3_text = '';
                }
                await entity.write('aqaraOpple', {0x0225: {value: payload, type: 0x41}}, manufacturerOptions.xiaomi);
                return {state: statearr};
            } else {
                throw new Error(`Not supported: '${key}'`);
            }
        },
    },
};
const definition = {
    zigbeeModel: ['lumi.airrtc.pcacn2'],
    model: 'S3',
    vendor: 'Aqara',
    description: 'Aqara S3 Smart Thermostat ZNWKQ11LM',
    fromZigbee: [fzLocal.ZNWKQ11LM, fz.temperature, fz.humidity],
    toZigbee: [tzLocal.ZNWKQ11LM],
    meta: {multiEndpoint: true},
    exposes: [e.temperature(), e.humidity(), e.power_outage_memory(), e.device_temperature().withAccess(ea.STATE),
        e.power_outage_count(),
        e.switch().withEndpoint('1'), e.switch().withEndpoint('2'), e.switch().withEndpoint('3'),
        exposes.binary('standby_enabled', ea.STATE_SET, true, false).withDescription('Enable standby'),
        exposes.enum('theme', ea.STATE_SET, ['classic', 'concise']).withDescription('Display theme'),
        exposes.enum('beep_volume', ea.STATE_SET, ['mute', 'high']).withDescription('Beep volume'),
        exposes.numeric('lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
            .withDescription('LCD brightness (will not persist if auto-brightness is enabled)'),
        exposes.enum('language', ea.STATE_SET, ['chinese', 'english']).withDescription('Interface language'),
        exposes.enum('screen_saver_style', ea.STATE_SET, ['Pointer clock', 'World photos', 'Time of the day']),
        exposes.numeric('standby_time', ea.STATE_SET).withValueMin(0).withValueMax(65534).withUnit('s')
            .withDescription('Display standby time'),
        exposes.enum('font_size', ea.STATE_SET, ['small', 'medium', 'large']).withDescription('Display font size'),
        exposes.binary('lcd_auto_brightness_enabled', ea.STATE_SET, true, false).withDescription('Enable LCD auto brightness'),
        exposes.enum('homepage', ea.STATE_SET, ['scene', 'feel', 'thermostat', 'switch']).withDescription('Default display homepage'),
        exposes.binary('screen_saver_enabled', ea.STATE_SET, true, false).withDescription('Enable screen saver'),
        exposes.numeric('standby_lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
            .withDescription('Standby LCD brightness'),
        exposes.enum('available_switches', ea.STATE_SET, ['none', '1', '2', '3', '1 and 2', '1 and 3', '2 and 3', 'all'])
            .withDescription('Control which switches are available in the switches screen (none disables switches screen)'),
        exposes.composite('switch_1_text_icon', 'switch_1_text_icon').withDescription('Switch 1 text and icon')
            .withFeature(exposes.enum('switch_1_icon', ea.STATE_SET, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'])
                .withDescription('Icon'))
            .withFeature(exposes.text('switch_1_text', ea.STATE_SET)
                .withDescription('Text')),
        exposes.composite('switch_2_text_icon', 'switch_2_text_icon').withDescription('Switch 2 text and icon')
            .withFeature(exposes.enum('switch_2_icon', ea.STATE_SET, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'])
                .withDescription('Icon'))
            .withFeature(exposes.text('switch_2_text', ea.STATE_SET)
                .withDescription('Text')),
        exposes.composite('switch_3_text_icon', 'switch_3_text_icon').withDescription('Switch 3 text and icon')
            .withFeature(exposes.enum('switch_3_icon', ea.STATE_SET, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'])
                .withDescription('Icon'))
            .withFeature(exposes.text('switch_3_text', ea.STATE_SET)
                .withDescription('Text')),
        exposes.numeric('lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
            .withDescription('LCD brightness (will not persist if auto-brightness is enabled)'),
    ],
//    meta: {disableDefaultResponse: true},
//    configure: async (device, coordinatorEndpoint, logger) => {
//        const endpoint = device.getEndpoint(5);
//        const binds = ['genBasic', 'genIdentify', 'genTime', 'hvacThermostat'];
//        await reporting.bind(endpoint, coordinatorEndpoint, binds);
//        await reporting.thermostatTemperature(endpoint, {min: 0, max: constants.repInterval.HOUR, change: 1});
//        await reporting.thermostatRunningState(endpoint);
//        await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
//        await reporting.thermostatTemperatureSetpointHold(endpoint);
//        await reporting.thermostatTemperatureSetpointHoldDuration(endpoint);

};

module.exports = definition;

Supported color modes

No response

Color temperature range

No response

masafra86 commented 1 year ago

About temperature, this should be the log lines I get when temperature changes: Received Zigbee message from 'Aqara S3 Thermostat', type 'attributeReport', cluster 'aqaraOpple', data '{"247":{"data":[3,40,38,5,33,54,0,9,33,0,8,10,33,211,176,13,35,15,11,19,0,14,35,0,0,0,0,200,32,3,201,39,252,254,63,24,52,8,234,11,154,32,0],"type":"Buffer"}}' from endpoint 1 with groupID null

but I'm not able to understand how to use them

masafra86 commented 1 year ago

I managed to understand the meaning of the string

Received Zigbee message from 'Aqara S3 Thermostat', type 'attributeReport', cluster 'aqaraOpple', data '{"586":0,"591":"0x09f60a5a183ffefc"}' from endpoint 1 with groupID null

Nut I'm not able to split it into pieces of informations, due to my lack of knowledge in Javascript. "586" is the system mode, that can be 'off' or 'heat' "591" has many different information: 09f6 is the temperature set on the thermostat (must be divided for 100) 0a5a is the local temperature sensed by the internal sensor (must be divided for 100) 1 is the state of the switch, that is on, but can be also 0, off 83ffefc is a mystery can anyone translate it for me in javascript? I need fromZigbee and toZigbee

masafra86 commented 1 year ago

The following is the work I tried to do on the S3 thermostat, and needs to be implemented. I still don't know how to set and receive the temperatures and the heat/off state

const fz = require('zigbee-herdsman-converters/converters/fromZigbee'); const tz = require('zigbee-herdsman-converters/converters/toZigbee'); const herdsman = require('zigbee-herdsman'); const exposes = require('zigbee-herdsman-converters/lib/exposes'); const reporting = require('zigbee-herdsman-converters/lib/reporting'); const ota = require('zigbee-herdsman-converters/lib/ota'); const e = exposes.presets; const ea = exposes.access;

const manufacturerOptions = {

xiaomi: {manufacturerCode: herdsman.Zcl.ManufacturerCode.LUMI_UNITED_TECH, disableDefaultResponse: true},

};

const fzLocal = {

ZNWKQ11LM: {
    cluster: 'aqaraOpple',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg, publish, options, meta) => {
        const result = {};
        if (msg.data.hasOwnProperty(0x0215)) {
            const lookup = {0: 'classic', 1: 'concise'};
            result.theme = lookup[msg.data[0x0215]];
        }
        if (msg.data.hasOwnProperty(0x0258)) {
            const lookup = {0: 'classic', 1: 'normal', 2: 'desert'};
            result.wallpaper = lookup[msg.data[0x0258]];
        }    
        // this part is not working, I tried but I don't know how to
        if (msg.data.hasOwnProperty(0x024f)) {
            const data = (msg.data.hasOwnProperty(0x024f));
            const systemMode = msg.data[0x024a] === 0 ? 'off' : 'heat';

            // Extract the temperature set on the thermostat (09f6) and convert it to a number
            const temperatureSet = parseInt(msg.data[0x024f].substr(2, 4), 16) / 100;

            // Extract the local temperature sensed by the internal sensor (0a5a) and convert it to a number
            const localTemperature = parseInt(msg.data[0x024f].substr(6, 4), 16) / 100;

            // Extract the state of the switch (1) and convert it to a boolean
            const switchState = msg.data[0x024f][10] === '1';

            // const localTemperature = parseInt(data.slice(6, 10), 16)/100;
            // const switchState = parseInt(data.slice(10, 11), 16);

            // const setpoint = parseInt(data.slice(2, 6), 16)/100;
            result.temperatureSet;
            result.localTemperature;
            result.switchState;
        } // the following is working properly
        if (msg.data.hasOwnProperty(0x024e)) {
            const lookup = {1: 'double tube air cond', 2: 'four tubes air cond', 3: 'floor heating',4: 'fresh air fan', 5: 'double tube air cond and floor heat', 6: 'floor heating and fresh air fan'};
            result.type_external_device = lookup[msg.data[0x024e]];
        }

        if (msg.data.hasOwnProperty(0x0214)) {
            const lookup = {1: 'pointer clock', 2: 'world photos', 3: 'time of the day'};
            result.screen_saver_style = lookup[msg.data[0x0214]];
        }
        if (msg.data.hasOwnProperty(0x0213)) {
            result.standby_enabled = msg.data[0x0213] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0254)) {
            const lookup = {1: 'mute', 0: 'high'};
            result.beep_volume = lookup[msg.data[0x0254]];
        }
        if (msg.data.hasOwnProperty(0x024A)) {
            const lookup = {0: 'idle', 1: 'heat'};
            result.system_mode = lookup[msg.data[0x024A]];
        }
        if (msg.data.hasOwnProperty(0x0255)) {
            result.volume = msg.data[0x0255];
        }

        if (msg.data.hasOwnProperty(0x217)) {
            const lookup = {3: 'small', 4: 'medium', 5: 'large'};
            result.font_size = lookup[msg.data[0x217]];
        }

        if (msg.data.hasOwnProperty(0x210)) {
            const lookup = {0: 'chinese', 1: 'english'};
            result.language = lookup[msg.data[0x210]];
        }
        if (msg.data.hasOwnProperty(0x216)) {
            result.standby_time = msg.data[0x216]/60;
        }
        if (msg.data.hasOwnProperty(0x0218)) {
            result.lcd_auto_brightness_enabled = msg.data[0x0218] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x221)) {
            result.screen_saver_enabled = msg.data[0x221] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0211)) {
            result.standby_lcd_brightness = msg.data[0x0211];
        }
        if (msg.data.hasOwnProperty(0x025A)) {
            result.night_mode = msg.data[0x025A] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0256)) {
            result.do_not_disturb = msg.data[0x0256] & 1 ? true : false;
        }
        return result;
    },
},

}; const tzLocal = {

ZNWKQ11LM: {
    key: ['theme', 'wallpaper', 
        'standby_enabled',
        'type_external_device',
        'system_mode',
        'beep_volume',
        'volume',
        'lcd_brightness',
        'language',
        'screen_saver_style',
        'standby_time',
        'font_size',
        'lcd_auto_brightness_enabled',
        'homepage',
        'screen_saver_enabled',
        'standby_lcd_brightness',
        'night_mode',
        'do_not_disturb',
    ],
    convertSet: async (entity, key, value, meta) => {
        if (key === 'standby_enabled') {
            await entity.write('aqaraOpple', {0x0213: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
            return {state: {standby_enabled: value}};
        /* } else if (key === 'setpointset') {
            function set_setpoint(setpointset) {
                return (setpointset+0x10000).toString(16).substr(-4).toUpperCase();
            }
            function set_localTemp(localTemp) {
                return (localTemp+0x10000).toString(16).substr(-4).toUpperCase();
            }
            function set_system_mode(system_mode) {
                return {state: {system_mode: value}};
            }
            const lookup = "0x" + set_setpoint(setpoint) + set_localTemp(localTemp) + set_system_mode(system_mode + "83ffefc");
            await entity.write('aqaraOpple', {0x024f: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {settemperature: value}, state: {localTemp: value}};

const systemMode = msg.data[0x024a] === 0 ? 'off' : 'heat'; */ } else if (key === 'temperatureSet') {

            // Again, not working

            // Extract the temperature set on the thermostat (09f6) and convert it to a number

            // Extract the local temperature sensed by the internal sensor (0a5a) and convert it to a number
            const localTemperature = parseInt(msg.data[0x024f].substr(6, 4), 16) / 100;

            // Extract the state of the switch (1) and convert it to a boolean
            const switchState = msg.data[0x024f][10] === '1';

            const mysteryValue = "83ffefc";
            const temperatureSetHex = (value * 100).toString(16).padStart(4, "0");
            const localTemperatureHex = (localTemperature * 100).toString(16).padStart(4, "0");

            const data = "0x${temperatureSetHex}${localTemperatureHex}${switchState}${mysteryValue}"

            await entity.write('aqaraOpple', {0x024f: {value: 0x0c80076c183ffefc, type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {temperatureSet: value}};
        } else if (key === 'occupied_heating_setpoint') {
            return {state: {thermostat_occupied_heating_setpoint: value}}; //not working
        } else if (key === 'system_mode') {
            const lookup = {'off': 0, 'heat': 1};
            await entity.write('aqaraOpple', {0x024a: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {system_mode: value}};
        } else if (key === 'wallpaper') {
            const lookup = {'classic': 0, 'normal': 1, 'desert': 2};
            await entity.write('aqaraOpple', {0x0258: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {wallpaper: value}};
        } else if (key === 'type_external_device') {
            const lookup = {'double tube air cond': 1, 'four tubes air cond': 2, 'floor heating': 3, 'fresh air fan': 4, 'double tube air cond and floor heat': 5, 'floor heating and fresh air fan': 6};
            await entity.write('aqaraOpple', {0x024e: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {type_external_device: value}};

        } else if (key === 'theme') {
            const lookup = {'classic': 0, 'concise': 1};
            await entity.write('aqaraOpple', {0x0215: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {theme: value}};
        } else if (key === 'beep_volume') {
            const lookup = {'mute': 0, 'high': 1};
            await entity.write('aqaraOpple', {0x0254: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {beep_volume: value}};
        } else if (key === 'volume') {
            await entity.write('aqaraOpple', {0x0255: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {volume: value}};
        } else if (key === 'lcd_brightness') {
            await entity.write('aqaraOpple', {0x0211: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {lcd_brightness: value}};
        } else if (key === 'language') {
            const lookup = {'chinese': 0, 'english': 1};
            await entity.write('aqaraOpple', {0x0210: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {language: value}};
        } else if (key === 'screen_saver_style') {
            const lookup = {'classic': 1, 'analog clock': 2};
            await entity.write('aqaraOpple', {0x0214: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {screen_saver_style: value}};
        } else if (key === 'standby_time') {
            await entity.write('aqaraOpple', {0x0216: {value: value, type: 0x23}*60}, manufacturerOptions.xiaomi);
            return {state: {standby_time: value}};
        } else if (key === 'font_size') {
            const lookup = {'small': 3, 'medium': 4, 'large': 5};
            await entity.write('aqaraOpple', {0x0217: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {font_size: value}};
        } else if (key === 'lcd_auto_brightness_enabled') {
            await entity.write('aqaraOpple', {0x0218: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
            return {state: {lcd_auto_brightness_enabled: value}};
        } else if (key === 'night_mode') {
            await entity.write('aqaraOpple', {0x025A: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
            return {state: {night_mode: value}};

        } else if (key === 'screen_saver_enabled') {
            await entity.write('aqaraOpple', {0x0221: {value: value, type: 0x10}}, manufacturerOptions.xiaomi);
            return {state: {screen_saver_enabled: value}};
        } else if (key === 'do_not_disturb') {
            await entity.write('aqaraOpple', {0x0256: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {do_not_disturb: value}};
        } else if (key === 'standby_lcd_brightness') {
            await entity.write('aqaraOpple', {0x0222: {value: value, type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {standby_lcd_brightness: value}};
        } else {
            throw new Error(`Not supported: '${key}'`);
        }
    },
},

}; const definition = { zigbeeModel: ['lumi.airrtc.pcacn2'], model: 'S3', vendor: 'Aqara', description: 'Aqara S3 Smart Thermostat ZNWKQ11LM', //the "from zigbee and to zigbeee" part needs to be implemented fromZigbee: [ fzLocal.ZNWKQ11LM, fz.temperature, fz.humidity, fz.aqara_opple, fz.thermostat, fz.hvac_user_interface], toZigbee: [tzLocal.ZNWKQ11LM, tz.thermostat_occupied_heating_setpoint], meta: {multiEndpoint: true}, exposes: [ e.humidity(), e.device_temperature().withAccess(ea.STATE), e.power_outage_count(), // e.switch().withEndpoint('1'), e.switch().withEndpoint('2'), e.switch().withEndpoint('3'),

    exposes.climate()
        .withSetpoint('occupied_heating_setpoint', 16, 32, 0.5, ea.STATE_SET).withLocalTemperature(ea.STATE)
        .withSystemMode(['off', 'heat'], ea.STATE_SET)
        .withRunningState(['idle', 'heat'], ea.STATE), // not working

    exposes.enum('type_external_device', ea.STATE_SET, ['double tube air cond', 'four tubes air cond', 'floor heating', 'fresh air fan', 'double tube air cond and floor heat', 'floor heating and fresh air fan']).withDescription('Type of switch'),
    exposes.enum('wallpaper', ea.STATE_SET, ['classic', 'normal', 'desert']).withDescription('Wallpaper'),

    exposes.enum('beep_volume', ea.STATE_SET, ['mute', 'high']).withDescription('Beep volume'),
    exposes.enum('system_mode', ea.STATE, ['idle', 'heat']).withDescription('System mode'),

    exposes.numeric('volume', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
        .withDescription('Volume (will not persist if Mute is enabled)'),
    exposes.numeric('lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
        .withDescription('LCD brightness (will not persist if auto-brightness is enabled)'),
    exposes.enum('language', ea.STATE_SET, ['chinese', 'english']).withDescription('Interface language'),
    exposes.enum('screen_saver_style', ea.STATE_SET, ['Pointer clock', 'World photos', 'Time of the day']),
    exposes.numeric('standby_time', ea.STATE_SET).withValueMin(0).withValueMax(300).withUnit('min')
        .withDescription('Display standby time'),
    exposes.enum('font_size', ea.STATE_SET, ['small', 'medium', 'large']).withDescription('Display font size'),
    exposes.binary('lcd_auto_brightness_enabled', ea.STATE_SET, true, false).withDescription('Enable LCD auto brightness'),
    exposes.binary('night_mode', ea.STATE_SET, true, false).withDescription('Enable night mode'),
    exposes.binary('do_not_disturb', ea.STATE_SET, true, false).withDescription('Enable Do not disturb'),

    exposes.binary('screen_saver_enabled', ea.STATE_SET, true, false).withDescription('Enable screen saver'),
    exposes.numeric('standby_lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%')
        .withDescription('Standby LCD brightness'),

],
meta: {disableDefaultResponse: true},

};

module.exports = definition;

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

masafra86 commented 1 year ago

I've tried to look for other devices that may work in a similar way, and I didn't find anything. Maybe the most similar is the Eigenstone Aqara S2 thermostat. I found an open issue here https://zigbee2mqtt.discourse.group/t/support-eigenstone-thermostat-s2-ktwkq03es/1099/2 but nothing more

masafra86 commented 1 year ago

Finally I managed to sniff the traffic directly from the Aqara G3 Hub and I'm now able to compile some more things on the thermostat. Now I can set the temperature on the thermostat and turn it on and off (that is basically what it is needed for), but I still can't get the local temperature. This is the converter I tinkered :

` const fz = require('zigbee-herdsman-converters/converters/fromZigbee'); const tz = require('zigbee-herdsman-converters/converters/toZigbee'); const herdsman = require('zigbee-herdsman'); const exposes = require('zigbee-herdsman-converters/lib/exposes'); const utils = require('zigbee-herdsman-converters/lib/utils'); const reporting = require('zigbee-herdsman-converters/lib/reporting'); const e = exposes.presets; const ea = exposes.access; const manufacturerOptions = { xiaomi: {manufacturerCode: herdsman.Zcl.ManufacturerCode.LUMI_UNITED_TECH, disableDefaultResponse: true}, }; const fzLocal = { ZNWKQ11LM: { cluster: 'aqaraOpple', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.hasOwnProperty(0x0215)) { const lookup = {0: 'classic', 1: 'concise'}; result.theme = lookup[msg.data[0x0215]]; } if (msg.data.hasOwnProperty(0x0258)) { const lookup = {0: 'classic', 1: 'normal', 2: 'desert'}; result.wallpaper = lookup[msg.data[0x0258]]; }
if (msg.data.hasOwnProperty(0x024f)) { const data = (msg.data.hasOwnProperty(0x024f));

            // Extract the temperature set on the thermostat and convert it to a number
            const occupied_heating_setpoint = parseInt(data.substr(2, 4), 16) / 100;

            // Extract the local temperature sensed by the internal sensor and convert it to a number
            const local_temperature = parseInt(data.substr(6, 4), 16) / 100;

            // Extract the state of the switch (1) and convert it to a boolean
            const switchState = data[10] === '1';
            const running_state = switchState === 0 ? 'idle' : 'heat';

            result.current_heating_setpoint = occupied_heating_setpoint;
            result.local_temperature = local_temperature;
            result.running_state = running_state;
        }
        if (msg.data.hasOwnProperty(0x024e)) {
            const lookup = {1: 'double tube air cond', 2: 'four tubes air cond', 3: 'floor heating',4: 'fresh air fan', 5: 'double tube air cond and floor heat', 6: 'floor heating and fresh air fan'};
            result.type_external_device = lookup[msg.data[0x024e]];
        }
        if (msg.data.hasOwnProperty(0x0214)) {
            const lookup = {1: 'pointer clock', 2: 'world photos', 3: 'time of the day'};
            result.screen_saver_style = lookup[msg.data[0x0214]];
        }
        if (msg.data.hasOwnProperty(0x0213)) {
            result.standby_enabled = msg.data[0x0213] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0254)) {
            const lookup = {1: 'mute', 0: 'high'};
            result.beep_volume = lookup[msg.data[0x0254]];
        }
        if (msg.data.hasOwnProperty(0x024A)) {
            const lookup = {0: 'idle', 1: 'heat'};
            result.system_mode = lookup[msg.data[0x024A]];
        }
        if (msg.data.hasOwnProperty(0x0255)) {
            result.volume = msg.data[0x0255];
        }
        if (msg.data.hasOwnProperty(0x217)) {
            const lookup = {3: 'small', 4: 'medium', 5: 'large'};
            result.font_size = lookup[msg.data[0x217]];
        }
        if (msg.data.hasOwnProperty(0x210)) {
            const lookup = {0: 'chinese', 1: 'english'};
            result.language = lookup[msg.data[0x210]];
        }
        if (msg.data.hasOwnProperty(0x216)) {
            result.standby_time = msg.data[0x216]/60;
        }
        if (msg.data.hasOwnProperty(0x0218)) {
            result.lcd_auto_brightness_enabled = msg.data[0x0218] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x221)) {
            result.screen_saver_enabled = msg.data[0x221] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0211)) {
            result.standby_lcd_brightness = msg.data[0x0211];
        }
        if (msg.data.hasOwnProperty(0x025A)) {
            result.night_mode = msg.data[0x025A] & 1 ? true : false;
        }
        if (msg.data.hasOwnProperty(0x0256)) {
            result.do_not_disturb = msg.data[0x0256] & 1 ? true : false;
        }
        return result;
    },
},

}; const tzLocal = { ZNWKQ11LM: { key: ['theme', 'wallpaper', 'standby_enabled', 'type_external_device', 'system_mode', 'beep_volume', 'volume', 'lcd_brightness', 'language', 'screen_saver_style', 'standby_time', 'font_size', 'lcd_auto_brightness_enabled', 'homepage', 'screen_saver_enabled', 'standby_lcd_brightness', 'night_mode', 'do_not_disturb', 'current_heating_setpoint', 'running_state' ], convertSet: async (entity, key, value, meta) => { if (key === 'standby_enabled') { await entity.write('aqaraOpple', {0x0213: {value: value, type: 0x10}}, manufacturerOptions.xiaomi); return {state: {standby_enabled: value}}; } else if (key === 'current_heating_setpoint') { const decimaloccupied = parseInt(value, 10)100; // convert the decimal value to integer const decimaloccupiedHex = decimaloccupied.toString(16).padStart(4, "0"); // convert the integer to hexadecimal and pad with zeros const data = 0x${decimaloccupiedHex}ffffffffffff; await entity.write('aqaraOpple', {0x024f: {value: data, type: 0x27}}, manufacturerOptions.xiaomi); return {state: {current_heating_setpoint: value}}; } else if (key === 'system_mode') { const lookup = {'off': 0, 'on': 1}; const data = 0xffffffff${lookup[value]}fffffff; await entity.write('aqaraOpple', {0x024f: {value: data, type: 0x27}}, manufacturerOptions.xiaomi); return {state: {running_state: value}}; } else if (key === 'wallpaper') { const lookup = {'classic': 0, 'normal': 1, 'desert': 2}; await entity.write('aqaraOpple', {0x0258: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {wallpaper: value}}; } else if (key === 'type_external_device') { const lookup = {'double tube air cond': 1, 'four tubes air cond': 2, 'floor heating': 3, 'fresh air fan': 4, 'double tube air cond and floor heat': 5, 'floor heating and fresh air fan': 6}; await entity.write('aqaraOpple', {0x024e: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {type_external_device: value}}; } else if (key === 'theme') { const lookup = {'classic': 0, 'concise': 1}; await entity.write('aqaraOpple', {0x0215: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {theme: value}}; } else if (key === 'beep_volume') { const lookup = {'mute': 0, 'high': 1}; await entity.write('aqaraOpple', {0x0254: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {beep_volume: value}}; } else if (key === 'volume') { await entity.write('aqaraOpple', {0x0255: {value: value, type: 0x20}}, manufacturerOptions.xiaomi); return {state: {volume: value}}; } else if (key === 'lcd_brightness') { await entity.write('aqaraOpple', {0x0211: {value: value, type: 0x20}}, manufacturerOptions.xiaomi); return {state: {lcd_brightness: value}}; } else if (key === 'language') { const lookup = {'chinese': 0, 'english': 1}; await entity.write('aqaraOpple', {0x0210: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {language: value}}; } else if (key === 'screen_saver_style') { const lookup = {'classic': 1, 'analog clock': 2}; await entity.write('aqaraOpple', {0x0214: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {screen_saver_style: value}}; } else if (key === 'standby_time') { await entity.write('aqaraOpple', {0x0216: {value: value, type: 0x23}60}, manufacturerOptions.xiaomi); return {state: {standby_time: value}}; } else if (key === 'font_size') { const lookup = {'small': 3, 'medium': 4, 'large': 5}; await entity.write('aqaraOpple', {0x0217: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi); return {state: {font_size: value}}; } else if (key === 'lcd_auto_brightness_enabled') { await entity.write('aqaraOpple', {0x0218: {value: value, type: 0x10}}, manufacturerOptions.xiaomi); return {state: {lcd_auto_brightness_enabled: value}}; } else if (key === 'night_mode') { await entity.write('aqaraOpple', {0x025A: {value: value, type: 0x10}}, manufacturerOptions.xiaomi); return {state: {night_mode: value}}; } else if (key === 'screen_saver_enabled') { await entity.write('aqaraOpple', {0x0221: {value: value, type: 0x10}}, manufacturerOptions.xiaomi); return {state: {screen_saver_enabled: value}}; } else if (key === 'do_not_disturb') { await entity.write('aqaraOpple', {0x0256: {value: value, type: 0x20}}, manufacturerOptions.xiaomi); return {state: {do_not_disturb: value}}; } else if (key === 'standby_lcd_brightness') { await entity.write('aqaraOpple', {0x0222: {value: value, type: 0x20}}, manufacturerOptions.xiaomi); return {state: {standby_lcd_brightness: value}}; } else { throw new Error(Not supported: '${key}'); } }, }, }; const definition = { zigbeeModel: ['lumi.airrtc.pcacn2'], model: 'S3', vendor: 'Aqara', description: 'Aqara S3 Smart Thermostat ZNWKQ11LM', //the "from zigbee and to zigbeee" part needs to be implemented fromZigbee: [ fzLocal.ZNWKQ11LM, fz.humidity ], toZigbee: [tzLocal.ZNWKQ11LM], meta: {multiEndpoint: true}, exposes: [ e.humidity(), e.device_temperature().withAccess(ea.STATE), e.power_outage_count(), // e.switch().withEndpoint('1'), e.switch().withEndpoint('2'), e.switch().withEndpoint('3'), exposes.climate().withSetpoint('current_heating_setpoint', 16, 32, 0.5, ea.STATE_SET).withLocalTemperature(ea.STATE), exposes.enum('type_external_device', ea.STATE_SET, ['double tube air cond', 'four tubes air cond', 'floor heating', 'fresh air fan', 'double tube air cond and floor heat', 'floor heating and fresh air fan']).withDescription('Type of switch'), exposes.enum('wallpaper', ea.STATE_SET, ['classic', 'normal', 'desert']).withDescription('Wallpaper'), exposes.enum('beep_volume', ea.STATE_SET, ['mute', 'high']).withDescription('Beep volume'), exposes.enum('system_mode', ea.STATE_SET, ['on', 'off']).withDescription('System mode'), exposes.enum('running_state', ea.STATE, ['idle', 'heating']).withDescription('Accensione'), exposes.numeric('volume', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%') .withDescription('Volume (will not persist if Mute is enabled)'), exposes.numeric('lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%') .withDescription('LCD brightness (will not persist if auto-brightness is enabled)'), exposes.enum('language', ea.STATE_SET, ['chinese', 'english']).withDescription('Interface language'), exposes.enum('screen_saver_style', ea.STATE_SET, ['Pointer clock', 'World photos', 'Time of the day']), exposes.numeric('standby_time', ea.STATE_SET).withValueMin(0).withValueMax(300).withUnit('min') .withDescription('Display standby time'), exposes.enum('font_size', ea.STATE_SET, ['small', 'medium', 'large']).withDescription('Display font size'), exposes.binary('lcd_auto_brightness_enabled', ea.STATE_SET, true, false).withDescription('Enable LCD auto brightness'), exposes.binary('night_mode', ea.STATE_SET, true, false).withDescription('Enable night mode'), exposes.binary('do_not_disturb', ea.STATE_SET, true, false).withDescription('Enable Do not disturb'), exposes.binary('screen_saver_enabled', ea.STATE_SET, true, false).withDescription('Enable screen saver'), exposes.numeric('standby_lcd_brightness', ea.STATE_SET).withValueMin(1).withValueMax(100).withUnit('%') .withDescription('Standby LCD brightness'), ], meta: {disableDefaultResponse: true}, }; module.exports = definition; `

masafra86 commented 1 year ago

I don't know how to solve the problem I face when I change the temperature on the climate. The device correctly changes the temperature, but on Z2MQTT I get an error "Exception while calling fromZigbee converter: data.substr is not a function}". I think this can be related to the fact that I'm not able to see the local temperature the thermostat

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days