"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const modernExtend_1 = require("../lib/modernExtend");
const zigbee_herdsman_1 = require("zigbee-herdsman");
const exposes = __importStar(require("../lib/exposes"));
const fromZigbee_1 = __importDefault(require("../converters/fromZigbee"));
const toZigbee_1 = __importDefault(require("../converters/toZigbee"));
const reporting = __importStar(require("../lib/reporting"));
const utils = __importStar(require("../lib/utils"));
const constants = __importStar(require("../lib/constants"));
const ota = __importStar(require("../lib/ota"));
const globalStore = __importStar(require("../lib/store"));
const logger_1 = require("../lib/logger");
const e = exposes.presets;
const ea = exposes.access;
const NS = 'zhc:bosch';
const manufacturerOptions = { manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH };
const sirenVolume = {
    'low': 0x01,
    'medium': 0x02,
    'high': 0x03,
};
const sirenLight = {
    'only_light': 0x00,
    'only_siren': 0x01,
    'siren_and_light': 0x02,
};
const outdoorSirenState = {
    'ON': 0x07,
    'OFF': 0x00,
};
const sirenPowerSupply = {
    'solar_panel': 0x01,
    'ac_power_supply': 0x02,
    'dc_power_supply': 0x03,
};
// Universal Switch II
const buttonMap = {
    config_led_top_left_press: 0x10,
    config_led_top_right_press: 0x11,
    config_led_bottom_left_press: 0x12,
    config_led_bottom_right_press: 0x13,
    config_led_top_left_longpress: 0x20,
    config_led_top_right_longpress: 0x21,
    config_led_bottom_left_longpress: 0x22,
    config_led_bottom_right_longpress: 0x23,
};
// Universal Switch II
const labelShortPress = `Specifies LED color (rgb) and pattern on short press as hex string.
0-2: RGB value (e.g. ffffff = white)
3: Light position (01=top, 02=bottom, 00=full)
4-7: Durations for sequence fade-in -> on -> fade-out -> off (e.g. 01020102)
8: Number of Repetitions (01=1 to ff=255)
Example: ff1493000104010001`;
// Universal Switch II
const labelLongPress = `Specifies LED color (rgb) and pattern on long press as hex string.
0-2: RGB value (e.g. ffffff = white)
3: Light position (01=top, 02=bottom, 00=full)
4-7: Durations for sequence fade-in -> on -> fade-out -> off (e.g. 01020102)
8: Number of Repetitions (01=1 to ff=255)
Example: ff4200000502050001`;
// Universal Switch II
const labelConfirmation = `Specifies LED color (rgb) and pattern of the confirmation response as hex string.
0-2: RGB value (e.g. ffffff = white)
3: Light position (01=top, 02=bottom, 00=full)
4-7: Durations for sequence fade-in -> on -> fade-out -> off (e.g. 01020102)
8: Number of Repetitions (01=1 to ff=255)
Example: 30ff00000102010001`;
const boschExtend = {
    hvacThermostatCluster: () => (0, modernExtend_1.deviceAddCustomCluster)('hvacThermostat', {
        ID: zigbee_herdsman_1.Zcl.Clusters.hvacThermostat.ID,
        attributes: {
            operatingMode: {
                ID: 0x4007,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            heatingDemand: {
                ID: 0x4020,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            valveAdaptStatus: {
                ID: 0x4022,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            remoteTemperature: {
                ID: 0x4040,
                type: zigbee_herdsman_1.Zcl.DataType.INT16,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            windowDetection: {
                ID: 0x4042,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            boostHeating: {
                ID: 0x4043,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
        },
        commands: {
            calibrateValve: {
                ID: 0x41,
                parameters: [],
            },
        },
        commandsResponse: {},
    }),
    hvacUserInterfaceCfgCluster: () => (0, modernExtend_1.deviceAddCustomCluster)('hvacUserInterfaceCfg', {
        ID: zigbee_herdsman_1.Zcl.Clusters.hvacUserInterfaceCfg.ID,
        attributes: {
            displayOrientation: {
                ID: 0x400b,
                type: zigbee_herdsman_1.Zcl.DataType.UINT8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            displayedTemperature: {
                ID: 0x4039,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            displayOntime: {
                ID: 0x403a,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
            displayBrightness: {
                ID: 0x403b,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
            },
        },
        commands: {},
        commandsResponse: {},
    }),
    operatingMode: () => (0, modernExtend_1.enumLookup)({
        name: 'operating_mode',
        cluster: 'hvacThermostat',
        attribute: 'operatingMode',
        reporting: { min: '10_SECONDS', max: 'MAX', change: null },
        description: 'Bosch-specific operating mode (overrides system mode)',
        lookup: { 'schedule': 0x00, 'manual': 0x01, 'pause': 0x05 },
        zigbeeCommandOptions: manufacturerOptions,
    }),
    windowDetection: () => (0, modernExtend_1.binary)({
        name: 'window_detection',
        cluster: 'hvacThermostat',
        attribute: 'windowDetection',
        description: 'Enable/disable window open (Lo.) mode',
        valueOn: ['ON', 0x01],
        valueOff: ['OFF', 0x00],
        zigbeeCommandOptions: manufacturerOptions,
    }),
    boostHeating: () => (0, modernExtend_1.binary)({
        name: 'boost_heating',
        cluster: 'hvacThermostat',
        attribute: 'boostHeating',
        reporting: { min: '10_SECONDS', max: 'MAX', change: null, attribute: 'boostHeating' },
        description: 'Activate boost heating (5 min. on TRV)',
        valueOn: ['ON', 0x01],
        valueOff: ['OFF', 0x00],
        zigbeeCommandOptions: manufacturerOptions,
    }),
    childLock: () => (0, modernExtend_1.binary)({
        name: 'child_lock',
        cluster: 'hvacUserInterfaceCfg',
        attribute: 'keypadLockout',
        description: 'Enables/disables physical input on the device',
        valueOn: ['LOCK', 0x01],
        valueOff: ['UNLOCK', 0x00],
    }),
    displayOntime: () => (0, modernExtend_1.numeric)({
        name: 'display_ontime',
        cluster: 'hvacUserInterfaceCfg',
        attribute: 'displayOntime',
        description: 'Sets the display on-time',
        valueMin: 5,
        valueMax: 30,
        unit: 's',
        zigbeeCommandOptions: manufacturerOptions,
    }),
    displayBrightness: () => (0, modernExtend_1.numeric)({
        name: 'display_brightness',
        cluster: 'hvacUserInterfaceCfg',
        attribute: 'displayBrightness',
        description: 'Sets brightness of the display',
        valueMin: 0,
        valueMax: 10,
        zigbeeCommandOptions: manufacturerOptions,
    }),
    valveAdaptProcess: () => {
        const adaptationStatus = {
            'none': 0x00,
            'ready_to_calibrate': 0x01,
            'calibration_in_progress': 0x02,
            'error': 0x03,
            'success': 0x04,
        };
        const exposes = [
            e.binary('valve_adapt_process', ea.ALL, true, false)
                .withLabel('Trigger adaptation process')
                .withDescription('Trigger the valve adaptation process. Only possible when adaptation status ' +
                'is "ready_to_calibrate" or "error".').withCategory('config'),
        ];
        const fromZigbee = [{
                cluster: 'hvacThermostat',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('valveAdaptStatus')) {
                        if (msg.data['valveAdaptStatus'] === adaptationStatus.calibration_in_progress) {
                            result.valve_adapt_process = true;
                        }
                        else {
                            result.valve_adapt_process = false;
                        }
                    }
                    return result;
                },
            }];
        const toZigbee = [{
                key: ['valve_adapt_process'],
                convertSet: async (entity, key, value, meta) => {
                    if (value == true) {
                        const adaptStatus = utils.getFromLookup(meta.state.valve_adapt_status, adaptationStatus);
                        switch (adaptStatus) {
                            case adaptationStatus.ready_to_calibrate:
                            case adaptationStatus.error:
                                await entity.command('hvacThermostat', 'calibrateValve', {}, manufacturerOptions);
                                break;
                            default:
                                throw new Error('Valve adaptation process not possible right now.');
                        }
                    }
                    return { state: { valve_adapt_process: value } };
                },
                convertGet: async (entity, key, meta) => {
                    await entity.read('hvacThermostat', ['valveAdaptStatus'], manufacturerOptions);
                },
            }];
        return {
            exposes,
            fromZigbee,
            toZigbee,
            isModernExtend: true,
        };
    },
    heatingDemand: () => {
        const fromZigbee = [{
                cluster: 'hvacThermostat',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('heatingDemand')) {
                        const demand = msg.data['heatingDemand'];
                        result.pi_heating_demand = demand;
                        result.running_state = demand > 0 ? 'heat' : 'idle';
                    }
                    return result;
                },
            }];
        const toZigbee = [{
                key: ['pi_heating_demand'],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'pi_heating_demand') {
                        let demand = utils.toNumber(value, key);
                        demand = utils.numberWithinRange(demand, 0, 100);
                        await entity.write('hvacThermostat', { heatingDemand: demand }, manufacturerOptions);
                        return { state: { pi_heating_demand: demand } };
                    }
                },
                convertGet: async (entity, key, meta) => {
                    await entity.read('hvacThermostat', ['heatingDemand'], manufacturerOptions);
                },
            }, {
                key: ['running_state'],
                convertGet: async (entity, key, meta) => {
                    await entity.read('hvacThermostat', ['heatingDemand'], manufacturerOptions);
                },
            }];
        return {
            fromZigbee,
            toZigbee,
            isModernExtend: true,
        };
    },
    ignoreDst: () => {
        const fromZigbee = [{
                cluster: 'genTime',
                type: 'read',
                convert: async (model, msg, publish, options, meta) => {
                    if (msg.data.includes('dstStart', 'dstEnd', 'dstShift')) {
                        const response = {
                            'dstStart': { attribute: 0x0003, status: zigbee_herdsman_1.Zcl.Status.SUCCESS, value: 0x00 },
                            'dstEnd': { attribute: 0x0004, status: zigbee_herdsman_1.Zcl.Status.SUCCESS, value: 0x00 },
                            'dstShift': { attribute: 0x0005, status: zigbee_herdsman_1.Zcl.Status.SUCCESS, value: 0x00 },
                        };
                        await msg.endpoint.readResponse(msg.cluster, msg.meta.zclTransactionSequenceNumber, response);
                    }
                },
            }];
        return {
            fromZigbee,
            isModernExtend: true,
        };
    },
    doorWindowContact: (hasVibrationSensor) => {
        const exposes = [
            e.binary('contact', ea.STATE, false, true)
                .withDescription('Indicates whether the device is opened or closed'),
            e.enum('action', ea.STATE, ['none', 'single', 'long'])
                .withDescription('Triggered action (e.g. a button click)').withCategory('diagnostic'),
        ];
        if (hasVibrationSensor) {
            exposes.push(e.binary('vibration', ea.STATE, true, false)
                .withDescription('Indicates whether the device detected vibration'));
        }
        const fromZigbee = [{
                cluster: 'ssIasZone',
                type: ['commandStatusChangeNotification', 'attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    if (msg.data.hasOwnProperty('zoneStatus') || msg.data.hasOwnProperty('zonestatus')) {
                        const zoneStatus = msg.type === 'commandStatusChangeNotification' ? msg.data.zonestatus : msg.data.zoneStatus;
                        const lookup = { 0x00: 'none', 0x01: 'single', 0x02: 'long' };
                        const result = {
                            contact: !((zoneStatus & 1) > 0),
                            vibration: (zoneStatus & 1 << 1) > 0,
                            tamper: (zoneStatus & 1 << 2) > 0,
                            battery_low: (zoneStatus & 1 << 3) > 0,
                            supervision_reports: (zoneStatus & 1 << 4) > 0,
                            restore_reports: (zoneStatus & 1 << 5) > 0,
                            trouble: (zoneStatus & 1 << 6) > 0,
                            ac_status: (zoneStatus & 1 << 7) > 0,
                            test: (zoneStatus & 1 << 8) > 0,
                            battery_defect: (zoneStatus & 1 << 9) > 0,
                            action: lookup[(zoneStatus >> 11) & 3],
                        };
                        if (result.action === 'none')
                            delete result.action;
                        return result;
                    }
                },
            }];
        return {
            exposes,
            fromZigbee,
            isModernExtend: true,
        };
    },
    smokeAlarm: () => {
        const smokeAlarm = {
            'OFF': 0x0000,
            'ON': 0x3c00, // 15360 or 46080 works
        };
        const burglarAlarm = {
            'OFF': 0x0001,
            'ON': 0xb401, // 46081
        };
        const exposes = [
            e.binary('smoke', ea.STATE, true, false).withDescription('Indicates whether the device detected smoke'),
            e.binary('test', ea.STATE, true, false).withDescription('Indicates whether the device is currently performing a test')
                .withCategory('diagnostic'),
            e.binary('alarm_smoke', ea.ALL, 'ON', 'OFF').withDescription('Toggle the smoke alarm siren').withCategory('config'),
            e.binary('alarm_burglar', ea.ALL, 'ON', 'OFF').withDescription('Toggle the burglar alarm siren').withCategory('config'),
        ];
        const fromZigbee = [{
                cluster: 'ssIasZone',
                type: ['commandStatusChangeNotification', 'attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    if (msg.data.hasOwnProperty('zoneStatus') || msg.data.hasOwnProperty('zonestatus')) {
                        const zoneStatus = msg.type === 'commandStatusChangeNotification' ? msg.data.zonestatus : msg.data.zoneStatus;
                        return {
                            smoke: (zoneStatus & 1) > 0,
                            alarm_smoke: (zoneStatus & 1 << 1) > 0,
                            battery_low: (zoneStatus & 1 << 3) > 0,
                            supervision_reports: (zoneStatus & 1 << 4) > 0,
                            restore_reports: (zoneStatus & 1 << 5) > 0,
                            alarm_burglar: (zoneStatus & 1 << 7) > 0,
                            test: (zoneStatus & 1 << 8) > 0,
                            alarm_silenced: (zoneStatus & 1 << 11) > 0,
                        };
                    }
                },
            }];
        const toZigbee = [{
                key: ['alarm_smoke', 'alarm_burglar'],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'alarm_smoke') {
                        const index = utils.getFromLookup(value, smokeAlarm);
                        await entity.command('ssIasZone', 'boschSmokeAlarmSiren', { data: index }, manufacturerOptions);
                        return { state: { alarm_smoke: value } };
                    }
                    if (key === 'alarm_burglar') {
                        const index = utils.getFromLookup(value, burglarAlarm);
                        await entity.command('ssIasZone', 'boschSmokeAlarmSiren', { data: index }, manufacturerOptions);
                        return { state: { alarm_burglar: value } };
                    }
                },
                convertGet: async (entity, key, meta) => {
                    switch (key) {
                        case 'alarm_smoke':
                        case 'alarm_burglar':
                        case 'zone_status':
                            await entity.read('ssIasZone', ['zoneStatus']);
                            break;
                        default:
                            throw new Error(`Unhandled key boschExtend.smokeAlarm.toZigbee.convertGet ${key}`);
                    }
                },
            }];
        return {
            exposes,
            fromZigbee,
            toZigbee,
            isModernExtend: true,
        };
    },
    broadcastAlarm: () => {
        const sirenState = {
            'smoke_off': 0x0000,
            'smoke_on': 0x3c00,
            'burglar_off': 0x0001,
            'burglar_on': 0xb401,
        };
        const exposes = [
            e.enum('broadcast_alarm', ea.SET, Object.keys(sirenState))
                .withDescription('Set siren state of all BSD-2 via broadcast').withCategory('config'),
        ];
        const toZigbee = [{
                key: ['broadcast_alarm'],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'broadcast_alarm') {
                        const index = utils.getFromLookup(value, sirenState);
                        utils.assertEndpoint(entity);
                        await entity.zclCommandBroadcast(255, zigbee_herdsman_1.ZSpec.BroadcastAddress.SLEEPY, zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID, 'boschSmokeAlarmSiren', { data: index }, manufacturerOptions);
                        return;
                    }
                },
            }];
        return {
            exposes,
            toZigbee,
            isModernExtend: true,
        };
    },
    twinguard: () => {
        const smokeSensitivity = {
            'low': 0x03,
            'medium': 0x02,
            'high': 0x01,
        };
        const sirenState = {
            'stop': 0x00,
            'pre_alarm': 0x01,
            'fire': 0x02,
            'burglar': 0x03,
        };
        const stateOffOn = {
            'OFF': 0x00,
            'ON': 0x01,
        };
        const exposes = [
            e.binary('smoke', ea.STATE, true, false).withDescription('Indicates whether the device detected smoke'),
            e.numeric('temperature', ea.STATE).withValueMin(0).withValueMax(65).withValueStep(0.1)
                .withUnit('°C').withDescription('Measured temperature value'),
            e.numeric('humidity', ea.STATE).withValueMin(0).withValueMax(100).withValueStep(0.1)
                .withUnit('%').withDescription('Measured relative humidity'),
            e.numeric('voc', ea.STATE).withValueMin(0).withValueMax(50000).withValueStep(1)
                .withLabel('VOC').withUnit('µg/m³').withDescription('Measured VOC value'),
            e.numeric('co2', ea.STATE).withValueMin(400).withValueMax(2400).withValueStep(1)
                .withLabel('CO2').withUnit('ppm').withDescription('The measured CO2 (carbon dioxide) value'),
            e.numeric('aqi', ea.STATE).withValueMin(0).withValueMax(500).withValueStep(1)
                .withLabel('AQI').withDescription('Air Quality Index'),
            e.numeric('illuminance_lux', ea.STATE).withUnit('lx')
                .withDescription('Measured illuminance in lux'),
            e.numeric('battery', ea.STATE).withUnit('%').withValueMin(0).withValueMax(100)
                .withDescription('Remaining battery in %').withCategory('diagnostic'),
            e.text('siren_state', ea.STATE)
                .withDescription('Siren state').withCategory('diagnostic'),
            e.enum('alarm', ea.ALL, Object.keys(sirenState)).withDescription('Alarm mode for siren'),
            e.binary('self_test', ea.ALL, true, false)
                .withDescription('Initiate self-test').withCategory('config'),
            e.enum('sensitivity', ea.ALL, Object.keys(smokeSensitivity))
                .withDescription('Sensitivity of the smoke detector').withCategory('config'),
            e.binary('pre_alarm', ea.ALL, 'ON', 'OFF')
                .withDescription('Enable/disable pre-alarm').withCategory('config'),
            e.binary('heartbeat', ea.ALL, 'ON', 'OFF')
                .withDescription('Enable/disable heartbeat (blue LED)').withCategory('config'),
        ];
        const fromZigbee = [{
                cluster: 'twinguardSmokeDetector',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('sensitivity')) {
                        result.sensitivity = (Object.keys(smokeSensitivity)[msg.data['sensitivity']]);
                    }
                    return result;
                },
            }, {
                cluster: 'twinguardMeasurements',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('humidity')) {
                        const humidity = utils.toNumber(msg.data['humidity']) / 100.0;
                        if (utils.isInRange(0, 100, humidity)) {
                            result.humidity = humidity;
                        }
                    }
                    if (msg.data.hasOwnProperty('airpurity')) {
                        const iaq = utils.toNumber(msg.data['airpurity']);
                        result.aqi = iaq;
                        let factorVoc = 6;
                        let factorCo2 = 2;
                        if ((iaq >= 51) && (iaq <= 100)) {
                            factorVoc = 10;
                            factorCo2 = 4;
                        }
                        else if ((iaq >= 101) && (iaq <= 150)) {
                            factorVoc = 20;
                            factorCo2 = 4;
                        }
                        else if ((iaq >= 151) && (iaq <= 200)) {
                            factorVoc = 50;
                            factorCo2 = 4;
                        }
                        else if ((iaq >= 201) && (iaq <= 250)) {
                            factorVoc = 100;
                            factorCo2 = 4;
                        }
                        else if (iaq >= 251) {
                            factorVoc = 100;
                            factorCo2 = 4;
                        }
                        result.voc = (iaq * factorVoc);
                        result.co2 = ((iaq * factorCo2) + 400);
                    }
                    if (msg.data.hasOwnProperty('temperature')) {
                        result.temperature = utils.toNumber(msg.data['temperature']) / 100.0;
                    }
                    if (msg.data.hasOwnProperty('illuminance_lux')) {
                        result.illuminance_lux = utils.precisionRound((msg.data['illuminance_lux'] / 2), 2);
                    }
                    if (msg.data.hasOwnProperty('battery')) {
                        result.battery = utils.precisionRound((msg.data['battery'] / 2), 2);
                    }
                    return result;
                },
            }, {
                cluster: 'twinguardOptions',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('pre_alarm')) {
                        result.pre_alarm = (Object.keys(stateOffOn)[msg.data['pre_alarm']]);
                    }
                    return result;
                },
            }, {
                cluster: 'twinguardSetup',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    if (msg.data.hasOwnProperty('heartbeat')) {
                        result.heartbeat = (Object.keys(stateOffOn)[msg.data['heartbeat']]);
                    }
                    return result;
                },
            }, {
                cluster: 'twinguardAlarm',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    const lookup = {
                        0x00200020: 'clear',
                        0x01200020: 'self_test',
                        0x02200020: 'burglar',
                        0x00200082: 'pre_alarm',
                        0x00200081: 'fire',
                        0x00200040: 'silenced',
                    };
                    if (msg.data.hasOwnProperty('alarm_status')) {
                        result.self_test = (msg.data['alarm_status'] & 1 << 24) > 0;
                        result.smoke = (msg.data['alarm_status'] & 1 << 7) > 0;
                        result.siren_state = lookup[msg.data['alarm_status']];
                    }
                    return result;
                },
            }, {
                cluster: 'genAlarms',
                type: ['commandAlarm', 'readResponse'],
                convert: async (model, msg, publish, options, meta) => {
                    const result = {};
                    const lookup = {
                        0x10: 'fire',
                        0x11: 'pre_alarm',
                        0x14: 'clear',
                        0x16: 'silenced',
                    };
                    result.siren_state = lookup[msg.data.alarmcode];
                    if (msg.data.alarmcode == 0x10 || msg.data.alarmcode == 0x11) {
                        await msg.endpoint.commandResponse('genAlarms', 'alarm', { alarmcode: msg.data.alarmcode, clusterid: 0xe000 }, { direction: 1 });
                    }
                    return result;
                },
            }];
        const toZigbee = [{
                key: ['sensitivity', 'pre_alarm', 'self_test', 'alarm', 'heartbeat'],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'sensitivity') {
                        const index = utils.getFromLookup(value, smokeSensitivity);
                        await entity.write('twinguardSmokeDetector', { sensitivity: index }, manufacturerOptions);
                        return { state: { sensitivity: value } };
                    }
                    if (key === 'pre_alarm') {
                        const index = utils.getFromLookup(value, stateOffOn);
                        await entity.write('twinguardOptions', { pre_alarm: index }, manufacturerOptions);
                        return { state: { pre_alarm: value } };
                    }
                    if (key === 'heartbeat') {
                        const endpoint = meta.device.getEndpoint(12);
                        const index = utils.getFromLookup(value, stateOffOn);
                        await endpoint.write('twinguardSetup', { heartbeat: index }, manufacturerOptions);
                        return { state: { heartbeat: value } };
                    }
                    if (key === 'self_test') {
                        if (value) {
                            await entity.command('twinguardSmokeDetector', 'initiateTestMode', manufacturerOptions);
                        }
                    }
                    if (key === 'alarm') {
                        const endpoint = meta.device.getEndpoint(12);
                        const index = utils.getFromLookup(value, sirenState);
                        utils.assertEndpoint(entity);
                        if (index == 0x00) {
                            await entity.commandResponse('genAlarms', 'alarm', { alarmcode: 0x16, clusterid: 0xe000 }, { direction: 1 });
                            await entity.commandResponse('genAlarms', 'alarm', { alarmcode: 0x14, clusterid: 0xe000 }, { direction: 1 });
                            await endpoint.command('twinguardAlarm', 'burglarAlarm', { data: 0x00 }, manufacturerOptions);
                        }
                        else if (index == 0x01) {
                            await entity.commandResponse('genAlarms', 'alarm', { alarmcode: 0x11, clusterid: 0xe000 }, { direction: 1 });
                            return { state: { siren_state: 'pre_alarm' } };
                        }
                        else if (index == 0x02) {
                            await entity.commandResponse('genAlarms', 'alarm', { alarmcode: 0x10, clusterid: 0xe000 }, { direction: 1 });
                            return { state: { siren_state: 'fire' } };
                        }
                        else if (index == 0x03) {
                            await endpoint.command('twinguardAlarm', 'burglarAlarm', { data: 0x01 }, manufacturerOptions);
                        }
                    }
                },
                convertGet: async (entity, key, meta) => {
                    switch (key) {
                        case 'sensitivity':
                            await entity.read('twinguardSmokeDetector', ['sensitivity'], manufacturerOptions);
                            break;
                        case 'pre_alarm':
                            await entity.read('twinguardOptions', ['pre_alarm'], manufacturerOptions);
                            break;
                        case 'heartbeat':
                            await meta.device.getEndpoint(12).read('twinguardSetup', ['heartbeat'], manufacturerOptions);
                            break;
                        case 'alarm':
                        case 'self_test':
                            await meta.device.getEndpoint(12).read('twinguardAlarm', ['alarm_status'], manufacturerOptions);
                            break;
                        default:
                            throw new Error(`Unhandled key boschExtend.twinguard.toZigbee.convertGet ${key}`);
                    }
                },
            }];
        return {
            exposes,
            fromZigbee,
            toZigbee,
            isModernExtend: true,
        };
    },
    bmct: () => {
        const stateDeviceMode = {
            'light': 0x04,
            'shutter': 0x01,
            'disabled': 0x00,
        };
        const stateMotor = {
            'stopped': 0x00,
            'opening': 0x01,
            'closing': 0x02,
        };
        const stateSwitchType = {
            'button': 0x01,
            'button_key_change': 0x02,
            'rocker_switch': 0x03,
            'rocker_switch_key_change': 0x04,
        };
        const stateOffOn = {
            'OFF': 0x00,
            'ON': 0x01,
        };
        const fromZigbee = [{
                cluster: 'boschSpecific',
                type: ['attributeReport', 'readResponse'],
                convert: (model, msg, publish, options, meta) => {
                    const result = {};
                    const data = msg.data;
                    if (data.hasOwnProperty('deviceMode')) {
                        result.device_mode = Object.keys(stateDeviceMode).find((key) => stateDeviceMode[key] === msg.data['deviceMode']);
                        const deviceMode = msg.data['deviceMode'];
                        if (deviceMode !== meta.device.meta.deviceMode) {
                            meta.device.meta.deviceMode = deviceMode;
                            meta.deviceExposesChanged();
                        }
                    }
                    if (data.hasOwnProperty('switchType')) {
                        result.switch_type = Object.keys(stateSwitchType).find((key) => stateSwitchType[key] === msg.data['switchType']);
                    }
                    if (data.hasOwnProperty('calibrationOpeningTime')) {
                        result.calibration_opening_time = msg.data['calibrationOpeningTime'] / 10;
                    }
                    if (data.hasOwnProperty('calibrationClosingTime')) {
                        result.calibration_closing_time = msg.data['calibrationClosingTime'] / 10;
                    }
                    if (data.hasOwnProperty('childLock')) {
                        const property = utils.postfixWithEndpointName('child_lock', msg, model, meta);
                        result[property] = msg.data['childLock'] === 1 ? 'ON' : 'OFF';
                    }
                    if (data.hasOwnProperty('motorState')) {
                        result.motor_state = Object.keys(stateMotor).find((key) => stateMotor[key] === msg.data['motorState']);
                    }
                    return result;
                },
            }];
        const toZigbee = [{
                key: [
                    'device_mode',
                    'switch_type',
                    'child_lock',
                    'state',
                    'on_time',
                    'off_wait_time',
                ],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'state') {
                        if ('ID' in entity && entity.ID === 1) {
                            await toZigbee_1.default.cover_state.convertSet(entity, key, value, meta);
                        }
                        else {
                            await toZigbee_1.default.on_off.convertSet(entity, key, value, meta);
                        }
                    }
                    if (key === 'on_time' || key === 'on_wait_time') {
                        if ('ID' in entity && entity.ID !== 1) {
                            await toZigbee_1.default.on_off.convertSet(entity, key, value, meta);
                        }
                    }
                    if (key === 'device_mode') {
                        const index = utils.getFromLookup(value, stateDeviceMode);
                        await entity.write('boschSpecific', { deviceMode: index });
                        await entity.read('boschSpecific', ['deviceMode']);
                        return { state: { device_mode: value } };
                    }
                    if (key === 'switch_type') {
                        const index = utils.getFromLookup(value, stateSwitchType);
                        await entity.write('boschSpecific', { switchType: index });
                        return { state: { switch_type: value } };
                    }
                    if (key === 'child_lock') {
                        const index = utils.getFromLookup(value, stateOffOn);
                        await entity.write('boschSpecific', { childLock: index });
                        return { state: { child_lock: value } };
                    }
                },
                convertGet: async (entity, key, meta) => {
                    switch (key) {
                        case 'state':
                        case 'on_time':
                        case 'off_wait_time':
                            if ('ID' in entity && entity.ID !== 1) {
                                await entity.read('genOnOff', ['onOff']);
                            }
                            break;
                        case 'device_mode':
                            await entity.read('boschSpecific', ['deviceMode']);
                            break;
                        case 'switch_type':
                            await entity.read('boschSpecific', ['switchType']);
                            break;
                        case 'child_lock':
                            await entity.read('boschSpecific', ['childLock']);
                            break;
                        default:
                            throw new Error(`Unhandled key boschExtend.bmct.toZigbee.convertGet ${key}`);
                    }
                },
            }, {
                key: ['calibration', 'calibration_closing_time', 'calibration_opening_time'],
                convertSet: async (entity, key, value, meta) => {
                    if (key === 'calibration_opening_time') {
                        const number = utils.toNumber(value, 'calibration_opening_time');
                        const index = number * 10;
                        await entity.write('boschSpecific', { calibrationOpeningTime: index });
                        return { state: { calibration_opening_time: number } };
                    }
                    if (key === 'calibration_closing_time') {
                        const number = utils.toNumber(value, 'calibration_closing_time');
                        const index = number * 10;
                        await entity.write('boschSpecific', { calibrationClosingTime: index });
                        return { state: { calibration_closing_time: number } };
                    }
                },
                convertGet: async (entity, key, meta) => {
                    switch (key) {
                        case 'calibration_opening_time':
                            await entity.read('boschSpecific', ['calibrationOpeningTime']);
                            break;
                        case 'calibration_closing_time':
                            await entity.read('boschSpecific', ['calibrationClosingTime']);
                            break;
                        default:
                            throw new Error(`Unhandled key boschExtend.bmct.toZigbee.convertGet ${key}`);
                    }
                },
            }];
        return {
            fromZigbee,
            toZigbee,
            isModernExtend: true,
        };
    },
};
const tzLocal = {
    rbshoszbeu: {
        key: ['light_delay', 'siren_delay', 'light_duration', 'siren_duration', 'siren_volume', 'alarm_state', 'power_source', 'siren_and_light'],
        convertSet: async (entity, key, value, meta) => {
            if (key === 'light_delay') {
                const index = value;
                await entity.write(0x0502, { 0xa004: { value: index, type: 0x21 } }, manufacturerOptions);
                return { state: { light_delay: value } };
            }
            if (key === 'siren_delay') {
                const index = value;
                await entity.write(0x0502, { 0xa003: { value: index, type: 0x21 } }, manufacturerOptions);
                return { state: { siren_delay: value } };
            }
            if (key === 'light_duration') {
                const index = value;
                await entity.write(0x0502, { 0xa005: { value: index, type: 0x20 } }, manufacturerOptions);
                return { state: { light_duration: value } };
            }
            if (key === 'siren_duration') {
                const index = value;
                await entity.write(0x0502, { 0xa000: { value: index, type: 0x20 } }, manufacturerOptions);
                return { state: { siren_duration: value } };
            }
            if (key === 'siren_and_light') {
                const index = utils.getFromLookup(value, sirenLight);
                await entity.write(0x0502, { 0xa001: { value: index, type: 0x20 } }, manufacturerOptions);
                return { state: { siren_and_light: value } };
            }
            if (key === 'siren_volume') {
                const index = utils.getFromLookup(value, sirenVolume);
                await entity.write(0x0502, { 0xa002: { value: index, type: 0x20 } }, manufacturerOptions);
                return { state: { siren_volume: value } };
            }
            if (key === 'power_source') {
                const index = utils.getFromLookup(value, sirenPowerSupply);
                await entity.write(0x0001, { 0xa002: { value: index, type: 0x20 } }, manufacturerOptions);
                return { state: { power_source: value } };
            }
            if (key === 'alarm_state') {
                const endpoint = meta.device.getEndpoint(1);
                const index = utils.getFromLookup(value, outdoorSirenState);
                if (index == 0) {
                    await endpoint.command(0x0502, 0xf0, { data: 0 }, manufacturerOptions);
                    return { state: { alarm_state: value } };
                }
                else {
                    await endpoint.command(0x0502, 0xf0, { data: 7 }, manufacturerOptions);
                    return { state: { alarm_state: value } };
                }
            }
        },
        convertGet: async (entity, key, meta) => {
            switch (key) {
                case 'light_delay':
                    await entity.read(0x0502, [0xa004], manufacturerOptions);
                    break;
                case 'siren_delay':
                    await entity.read(0x0502, [0xa003], manufacturerOptions);
                    break;
                case 'light_duration':
                    await entity.read(0x0502, [0xa005], manufacturerOptions);
                    break;
                case 'siren_duration':
                    await entity.read(0x0502, [0xa000], manufacturerOptions);
                    break;
                case 'siren_and_light':
                    await entity.read(0x0502, [0xa001], manufacturerOptions);
                    break;
                case 'siren_volume':
                    await entity.read(0x0502, [0xa002], manufacturerOptions);
                    break;
                case 'alarm_state':
                    await entity.read(0x0502, [0xf0], manufacturerOptions);
                    break;
                default: // Unknown key
                    throw new Error(`Unhandled key toZigbee.rbshoszbeu.convertGet ${key}`);
            }
        },
    },
    bhius_config: {
        key: Object.keys(buttonMap),
        convertGet: async (entity, key, meta) => {
            if (!buttonMap.hasOwnProperty(key)) {
                throw new Error(`Unknown key ${key}`);
            }
            await entity.read('boschSpecific', [buttonMap[key]], manufacturerOptions);
        },
        convertSet: async (entity, key, value, meta) => {
            if (!buttonMap.hasOwnProperty(key)) {
                return;
            }
            const buffer = Buffer.from(value, 'hex');
            if (buffer.length !== 9)
                throw new Error(`Invalid configuration length: ${buffer.length} (should be 9)`);
            const payload = {};
            payload[buttonMap[key]] = { value: buffer, type: 65 };
            await entity.write('boschSpecific', payload, manufacturerOptions);
            const result = {};
            result[key] = value;
            return { state: result };
        },
    },
};
const fzLocal = {
    bhius_button_press: {
        cluster: 'boschSpecific',
        type: 'raw',
        options: [e.text('led_response', ea.ALL).withLabel('LED config (confirmation response)').withDescription(labelConfirmation)],
        convert: async (model, msg, publish, options, meta) => {
            const sequenceNumber = msg.data.readUInt8(3);
            const buttonId = msg.data.readUInt8(4);
            const longPress = msg.data.readUInt8(5);
            const duration = msg.data.readUInt16LE(6);
            let buffer;
            if (options.hasOwnProperty('led_response')) {
                buffer = Buffer.from(options.led_response, 'hex');
                if (buffer.length !== 9) {
                    logger_1.logger.error(`Invalid length of led_response: ${buffer.length} (should be 9)`, NS);
                    buffer = Buffer.from('30ff00000102010001', 'hex');
                }
            }
            else {
                buffer = Buffer.from('30ff00000102010001', 'hex');
            }
            if (utils.hasAlreadyProcessedMessage(msg, model, sequenceNumber))
                return;
            const buttons = { 0: 'top_left', 1: 'top_right', 2: 'bottom_left', 3: 'bottom_right' };
            let command = '';
            if (buttonId in buttons) {
                if (longPress && duration > 0) {
                    if (globalStore.hasValue(msg.endpoint, buttons[buttonId]))
                        return;
                    globalStore.putValue(msg.endpoint, buttons[buttonId], duration);
                    command = 'longpress';
                }
                else {
                    globalStore.clearValue(msg.endpoint, buttons[buttonId]);
                    command = longPress ? 'longpress_release' : 'release';
                    msg.endpoint.command('boschSpecific', 'confirmButtonPressed', { data: buffer }, { sendPolicy: 'immediate' })
                        .catch((error) => { });
                }
                return { action: `button_${buttons[buttonId]}_${command}` };
            }
            else {
                logger_1.logger.error(`Received message with unknown command ID ${buttonId}. Data: 0x${msg.data.toString('hex')}`, NS);
            }
        },
    },
    bhius_config: {
        cluster: 'boschSpecific',
        type: ['attributeReport', 'readResponse'],
        convert: async (model, msg, publish, options, meta) => {
            const result = {};
            for (const id of Object.values(buttonMap)) {
                if (msg.data.hasOwnProperty(id)) {
                    result[Object.keys(buttonMap).find((key) => buttonMap[key] === id)] = msg.data[id].toString('hex');
                }
            }
            return result;
        },
    },
};
const definitions = [
    {
        zigbeeModel: ['RBSH-OS-ZB-EU'],
        model: 'BSIR-EZ',
        vendor: 'Bosch',
        description: 'Outdoor siren',
        fromZigbee: [fromZigbee_1.default.ias_alarm_only_alarm_1, fromZigbee_1.default.battery, fromZigbee_1.default.power_source],
        toZigbee: [tzLocal.rbshoszbeu, toZigbee_1.default.warning],
        meta: { battery: { voltageToPercentage: { min: 2500, max: 4200 } } },
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic']);
            await reporting.batteryVoltage(endpoint);
            await endpoint.read(0x0502, [0xa000, 0xa001, 0xa002, 0xa003, 0xa004, 0xa005], manufacturerOptions);
            await endpoint.unbind('genPollCtrl', coordinatorEndpoint);
        },
        exposes: [
            e.binary('alarm_state', ea.ALL, 'ON', 'OFF').withDescription('Alarm turn ON/OFF'),
            e.numeric('light_delay', ea.ALL).withValueMin(0).withValueMax(30).withValueStep(1)
                .withUnit('s').withDescription('Flashing light delay').withUnit('s'),
            e.numeric('siren_delay', ea.ALL).withValueMin(0).withValueMax(30).withValueStep(1)
                .withUnit('s').withDescription('Siren alarm delay').withUnit('s'),
            e.numeric('siren_duration', ea.ALL).withValueMin(1).withValueMax(15).withValueStep(1)
                .withUnit('m').withDescription('Duration of the alarm siren').withUnit('m'),
            e.numeric('light_duration', ea.ALL).withValueMin(1).withValueMax(15).withValueStep(1)
                .withUnit('m').withDescription('Duration of the alarm light').withUnit('m'),
            e.enum('siren_volume', ea.ALL, Object.keys(sirenVolume)).withDescription('Volume of the alarm'),
            e.enum('siren_and_light', ea.ALL, Object.keys(sirenLight)).withDescription('Siren and Light behaviour during alarm '),
            e.enum('power_source', ea.ALL, Object.keys(sirenPowerSupply)).withDescription('Siren power source'),
            e.warning()
                .removeFeature('strobe_level')
                .removeFeature('strobe')
                .removeFeature('strobe_duty_cycle')
                .removeFeature('level')
                .removeFeature('duration'),
            e.test(), e.tamper(), e.battery(), e.battery_voltage(), e.battery_low(),
            e.binary('ac_status', ea.STATE, true, false).withDescription('Is the device plugged in'),
        ],
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('ssIasZone', {
                ID: zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID,
                attributes: {},
                commands: {
                    boschTestTamper: {
                        ID: 0xF3,
                        parameters: [
                            { name: 'data', type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
                        ],
                    },
                },
                commandsResponse: {},
            }),
            (0, modernExtend_1.deviceAddCustomCluster)('ssIasWd', {
                ID: zigbee_herdsman_1.Zcl.Clusters.ssIasWd.ID,
                attributes: {},
                commands: {
                    boschOutdoorSiren: {
                        ID: 240,
                        parameters: [
                            { name: 'data', type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
                        ],
                    },
                },
                commandsResponse: {},
            }),
            (0, modernExtend_1.quirkCheckinInterval)(0),
        ],
    },
    {
        zigbeeModel: ['RBSH-WS-ZB-EU'],
        model: 'BWA-1',
        vendor: 'Bosch',
        description: 'Smart water alarm',
        exposes: [],
        fromZigbee: [],
        toZigbee: [],
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('boschSpecific', {
                ID: 0xfcac,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    alarmOnMotion: {
                        ID: 0x0003,
                        type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN,
                    },
                },
                commands: {},
                commandsResponse: {},
            }),
            (0, modernExtend_1.iasZoneAlarm)({
                zoneType: 'water_leak',
                zoneAttributes: ['tamper'],
            }),
            (0, modernExtend_1.battery)({
                percentage: true,
                lowStatus: true,
            }),
            (0, modernExtend_1.binary)({
                name: 'alarm_on_motion',
                cluster: 'boschSpecific',
                attribute: 'alarmOnMotion',
                description: 'Toggle audible alarm on motion',
                valueOn: ['ON', 0x01],
                valueOff: ['OFF', 0x00],
                zigbeeCommandOptions: manufacturerOptions,
                entityCategory: 'config',
            }),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
            await endpoint.read('ssIasZone', ['zoneStatus']);
            await endpoint.read('boschSpecific', ['alarmOnMotion'], manufacturerOptions);
        },
    },
    {
        zigbeeModel: ['RBSH-SD-ZB-EU'],
        model: 'BSD-2',
        vendor: 'Bosch',
        description: 'Smoke alarm II',
        exposes: [],
        fromZigbee: [],
        toZigbee: [],
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('ssIasZone', {
                ID: zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID,
                attributes: {},
                commands: {
                    boschSmokeAlarmSiren: {
                        ID: 0x80,
                        parameters: [{ name: 'data', type: zigbee_herdsman_1.Zcl.DataType.UINT16 }],
                    },
                },
                commandsResponse: {},
            }),
            boschExtend.smokeAlarm(),
            (0, modernExtend_1.battery)({
                percentage: true,
                lowStatus: false,
            }),
            (0, modernExtend_1.enumLookup)({
                name: 'sensitivity',
                cluster: 'ssIasZone',
                attribute: 'currentZoneSensitivityLevel',
                description: 'Sensitivity of the smoke detector',
                lookup: {
                    'low': 0x00,
                    'medium': 0x01,
                    'high': 0x02,
                },
                entityCategory: 'config',
            }),
            boschExtend.broadcastAlarm(),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
            await endpoint.read('ssIasZone', ['zoneStatus']);
            await endpoint.read('ssIasZone', ['currentZoneSensitivityLevel']);
        },
    },
    {
        zigbeeModel: ['RFDL-ZB', 'RFDL-ZB-EU', 'RFDL-ZB-H', 'RFDL-ZB-K', 'RFDL-ZB-CHI', 'RFDL-ZB-MS', 'RFDL-ZB-ES', 'RFPR-ZB',
            'RFPR-ZB-EU', 'RFPR-ZB-CHI', 'RFPR-ZB-ES', 'RFPR-ZB-MS'],
        model: 'RADON TriTech ZB',
        vendor: 'Bosch',
        description: 'Wireless motion detector',
        fromZigbee: [fromZigbee_1.default.temperature, fromZigbee_1.default.battery, fromZigbee_1.default.ias_occupancy_alarm_1, fromZigbee_1.default.illuminance],
        toZigbee: [],
        meta: { battery: { voltageToPercentage: '3V_2500' } },
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg', 'msIlluminanceMeasurement']);
            await reporting.temperature(endpoint);
            await reporting.batteryVoltage(endpoint);
            await reporting.illuminance(endpoint);
        },
        exposes: [e.temperature(), e.battery(), e.occupancy(), e.battery_low(), e.tamper(), e.illuminance(), e.illuminance_lux()],
    },
    {
        zigbeeModel: ['ISW-ZPR1-WP13'],
        model: 'ISW-ZPR1-WP13',
        vendor: 'Bosch',
        description: 'Motion sensor',
        fromZigbee: [fromZigbee_1.default.temperature, fromZigbee_1.default.battery, fromZigbee_1.default.ias_occupancy_alarm_1, fromZigbee_1.default.ignore_iaszone_report],
        toZigbee: [],
        meta: { battery: { voltageToPercentage: '3V_2500' } },
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(5);
            await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg']);
            await reporting.temperature(endpoint);
            await reporting.batteryVoltage(endpoint);
        },
        exposes: [e.temperature(), e.battery(), e.occupancy(), e.battery_low(), e.tamper()],
    },
    {
        zigbeeModel: ['RBSH-TRV0-ZB-EU'],
        model: 'BTH-RA',
        vendor: 'Bosch',
        description: 'Radiator thermostat II',
        ota: ota.zigbeeOTA,
        exposes: [
            e.climate()
                .withLocalTemperature(ea.STATE_GET, 'Temperature used by the heating algorithm. ' +
                'This is the temperature measured on the device (by default) or the remote temperature (if set within the last 30 min).')
                .withLocalTemperatureCalibration(-5, 5, 0.1)
                .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
                .withSystemMode(['heat'])
                .withPiHeatingDemand(ea.ALL)
                .withRunningState(['idle', 'heat'], ea.STATE_GET),
        ],
        fromZigbee: [
            fromZigbee_1.default.thermostat,
        ],
        toZigbee: [
            toZigbee_1.default.thermostat_system_mode,
            toZigbee_1.default.thermostat_occupied_heating_setpoint,
            toZigbee_1.default.thermostat_local_temperature_calibration,
            toZigbee_1.default.thermostat_local_temperature,
            toZigbee_1.default.thermostat_keypad_lockout,
        ],
        extend: [
            boschExtend.hvacThermostatCluster(),
            boschExtend.hvacUserInterfaceCfgCluster(),
            (0, modernExtend_1.battery)({
                percentage: true,
                lowStatus: false,
            }),
            boschExtend.operatingMode(),
            boschExtend.windowDetection(),
            boschExtend.boostHeating(),
            (0, modernExtend_1.numeric)({
                name: 'remote_temperature',
                cluster: 'hvacThermostat',
                attribute: 'remoteTemperature',
                description: 'Input for remote temperature sensor. ' +
                    'Required at least every 30 min. to prevent fallback to internal sensor!',
                valueMin: 0.0,
                valueMax: 35.0,
                valueStep: 0.01,
                unit: '°C',
                scale: 100,
                zigbeeCommandOptions: manufacturerOptions,
            }),
            (0, modernExtend_1.enumLookup)({
                name: 'setpoint_change_source',
                cluster: 'hvacThermostat',
                attribute: 'setpointChangeSource',
                reporting: { min: '10_SECONDS', max: 'MAX', change: null },
                description: 'Source of the current setpoint temperature',
                lookup: { 'manual': 0x00, 'schedule': 0x01, 'externally': 0x02 },
                access: 'STATE_GET',
            }),
            boschExtend.childLock(),
            boschExtend.displayOntime(),
            boschExtend.displayBrightness(),
            (0, modernExtend_1.enumLookup)({
                name: 'display_orientation',
                cluster: 'hvacUserInterfaceCfg',
                attribute: 'displayOrientation',
                description: 'Sets orientation of the display',
                lookup: { 'normal': 0x00, 'flipped': 0x01 },
                zigbeeCommandOptions: manufacturerOptions,
            }),
            (0, modernExtend_1.enumLookup)({
                name: 'displayed_temperature',
                cluster: 'hvacUserInterfaceCfg',
                attribute: 'displayedTemperature',
                description: 'Temperature displayed on the TRV',
                lookup: { 'target': 0x00, 'measured': 0x01 },
                zigbeeCommandOptions: manufacturerOptions,
            }),
            (0, modernExtend_1.enumLookup)({
                name: 'valve_adapt_status',
                cluster: 'hvacThermostat',
                attribute: 'valveAdaptStatus',
                reporting: { min: '10_SECONDS', max: 'MAX', change: null },
                description: 'Specifies the current status of the valve adaptation',
                lookup: {
                    'none': 0x00,
                    'ready_to_calibrate': 0x01,
                    'calibration_in_progress': 0x02,
                    'error': 0x03,
                    'success': 0x04,
                },
                zigbeeCommandOptions: manufacturerOptions,
                access: 'STATE_GET',
            }),
            boschExtend.valveAdaptProcess(),
            boschExtend.heatingDemand(),
            boschExtend.ignoreDst(),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, [
                'hvacThermostat', 'hvacUserInterfaceCfg',
            ]);
            await reporting.thermostatTemperature(endpoint);
            await reporting.thermostatOccupiedHeatingSetpoint(endpoint, {
                min: constants.repInterval.SECONDS_10,
                max: constants.repInterval.HOUR,
                change: 50,
            });
            await reporting.thermostatKeypadLockMode(endpoint);
            await endpoint.configureReporting('hvacThermostat', [{
                    attribute: 'heatingDemand',
                    minimumReportInterval: constants.repInterval.SECONDS_10,
                    maximumReportInterval: constants.repInterval.MAX,
                    reportableChange: null,
                }], manufacturerOptions);
            await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
            await endpoint.read('hvacThermostat', ['localTemperatureCalibration', 'setpointChangeSource']);
            await endpoint.read('hvacThermostat', [
                'operatingMode', 'heatingDemand', 'valveAdaptStatus', 'remoteTemperature', 'windowDetection', 'boostHeating',
            ], manufacturerOptions);
            await endpoint.read('hvacUserInterfaceCfg', ['keypadLockout']);
            await endpoint.read('hvacUserInterfaceCfg', [
                'displayOrientation', 'displayedTemperature', 'displayOntime', 'displayBrightness',
            ], manufacturerOptions);
        },
    },
    {
        zigbeeModel: ['RBSH-RTH0-BAT-ZB-EU'],
        model: 'BTH-RM',
        vendor: 'Bosch',
        description: 'Room thermostat II (Battery model)',
        exposes: [
            e.climate()
                .withLocalTemperature()
                .withSetpoint('occupied_heating_setpoint', 4.5, 30, 0.5)
                .withSetpoint('occupied_cooling_setpoint', 4.5, 30, 0.5)
                .withLocalTemperatureCalibration(-5, 5, 0.1)
                .withSystemMode(['off', 'heat', 'cool'])
                .withRunningState(['idle', 'heat', 'cool']),
        ],
        fromZigbee: [
            fromZigbee_1.default.thermostat,
            fromZigbee_1.default.hvac_user_interface,
        ],
        toZigbee: [
            toZigbee_1.default.thermostat_system_mode,
            toZigbee_1.default.thermostat_running_state,
            toZigbee_1.default.thermostat_occupied_heating_setpoint,
            toZigbee_1.default.thermostat_occupied_cooling_setpoint,
            toZigbee_1.default.thermostat_programming_operation_mode, // NOTE: Only 0x0 & 0x1 supported
            toZigbee_1.default.thermostat_local_temperature_calibration,
            toZigbee_1.default.thermostat_local_temperature,
            toZigbee_1.default.thermostat_temperature_setpoint_hold,
            toZigbee_1.default.thermostat_temperature_display_mode,
        ],
        extend: [
            boschExtend.hvacThermostatCluster(),
            boschExtend.hvacUserInterfaceCfgCluster(),
            (0, modernExtend_1.battery)({
                voltageToPercentage: { min: 4400, max: 6400 },
                percentage: true,
                voltage: true,
                lowStatus: false,
                voltageReporting: true,
                percentageReporting: false,
            }),
            (0, modernExtend_1.humidity)(),
            boschExtend.operatingMode(),
            boschExtend.windowDetection(),
            boschExtend.boostHeating(),
            boschExtend.childLock(),
            boschExtend.displayOntime(),
            boschExtend.displayBrightness(),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, [
                'hvacThermostat', 'hvacUserInterfaceCfg',
            ]);
            await reporting.thermostatSystemMode(endpoint);
            await reporting.thermostatRunningState(endpoint);
            await reporting.thermostatTemperature(endpoint);
            await reporting.thermostatOccupiedHeatingSetpoint(endpoint, {
                min: constants.repInterval.SECONDS_10,
                max: constants.repInterval.HOUR,
                change: 50,
            });
            await reporting.thermostatOccupiedCoolingSetpoint(endpoint, {
                min: constants.repInterval.SECONDS_10,
                max: constants.repInterval.HOUR,
                change: 50,
            });
            await reporting.thermostatKeypadLockMode(endpoint);
            await endpoint.read('genPowerCfg', ['batteryVoltage']);
            await endpoint.read('hvacThermostat', ['localTemperatureCalibration']);
            await endpoint.read('hvacThermostat', ['operatingMode', 'windowDetection', 'boostHeating'], manufacturerOptions);
            await endpoint.read('hvacUserInterfaceCfg', ['keypadLockout']);
            await endpoint.read('hvacUserInterfaceCfg', ['displayOntime', 'displayBrightness'], manufacturerOptions);
        },
    },
    {
        zigbeeModel: ['RBSH-RTH0-ZB-EU'],
        model: 'BTH-RM230Z',
        vendor: 'Bosch',
        description: 'Room thermostat II 230V',
        exposes: [
            e.climate()
                .withLocalTemperature()
                .withSetpoint('occupied_heating_setpoint', 4.5, 30, 0.5)
                .withSetpoint('occupied_cooling_setpoint', 4.5, 30, 0.5)
                .withLocalTemperatureCalibration(-5, 5, 0.1)
                .withSystemMode(['off', 'heat', 'cool'])
                .withRunningState(['idle', 'heat', 'cool']),
        ],
        fromZigbee: [
            fromZigbee_1.default.thermostat,
            fromZigbee_1.default.hvac_user_interface,
        ],
        toZigbee: [
            toZigbee_1.default.thermostat_system_mode,
            toZigbee_1.default.thermostat_running_state,
            toZigbee_1.default.thermostat_occupied_heating_setpoint,
            toZigbee_1.default.thermostat_occupied_cooling_setpoint,
            toZigbee_1.default.thermostat_programming_operation_mode, // NOTE: Only 0x0 & 0x1 supported
            toZigbee_1.default.thermostat_local_temperature_calibration,
            toZigbee_1.default.thermostat_local_temperature,
            toZigbee_1.default.thermostat_temperature_setpoint_hold,
            toZigbee_1.default.thermostat_temperature_display_mode,
        ],
        extend: [
            boschExtend.hvacThermostatCluster(),
            boschExtend.hvacUserInterfaceCfgCluster(),
            (0, modernExtend_1.humidity)(),
            boschExtend.operatingMode(),
            boschExtend.windowDetection(),
            boschExtend.boostHeating(),
            boschExtend.childLock(),
            boschExtend.displayOntime(),
            boschExtend.displayBrightness(),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, [
                'hvacThermostat', 'hvacUserInterfaceCfg',
            ]);
            await reporting.thermostatSystemMode(endpoint);
            await reporting.thermostatRunningState(endpoint);
            await reporting.thermostatTemperature(endpoint);
            await reporting.thermostatOccupiedHeatingSetpoint(endpoint, {
                min: constants.repInterval.SECONDS_10,
                max: constants.repInterval.HOUR,
                change: 50,
            });
            await reporting.thermostatOccupiedCoolingSetpoint(endpoint, {
                min: constants.repInterval.SECONDS_10,
                max: constants.repInterval.HOUR,
                change: 50,
            });
            await reporting.thermostatKeypadLockMode(endpoint);
            await endpoint.read('hvacThermostat', ['localTemperatureCalibration']);
            await endpoint.read('hvacThermostat', ['operatingMode', 'windowDetection', 'boostHeating'], manufacturerOptions);
            await endpoint.read('hvacUserInterfaceCfg', ['keypadLockout']);
            await endpoint.read('hvacUserInterfaceCfg', ['displayOntime', 'displayBrightness'], manufacturerOptions);
        },
    },
    {
        zigbeeModel: ['Champion'],
        model: '8750001213',
        vendor: 'Bosch',
        description: 'Twinguard',
        exposes: [],
        fromZigbee: [],
        toZigbee: [],
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('twinguardSmokeDetector', {
                ID: 0xe000,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    sensitivity: { ID: 0x4003, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                },
                commands: {
                    initiateTestMode: {
                        ID: 0x00,
                        parameters: [],
                    },
                },
                commandsResponse: {},
            }),
            (0, modernExtend_1.deviceAddCustomCluster)('twinguardMeasurements', {
                ID: 0xe002,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    humidity: { ID: 0x4000, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown1: { ID: 0x4001, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown2: { ID: 0x4002, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    airpurity: { ID: 0x4003, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    temperature: { ID: 0x4004, type: zigbee_herdsman_1.Zcl.DataType.INT16 },
                    illuminance_lux: { ID: 0x4005, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    battery: { ID: 0x4006, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown3: { ID: 0x4007, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown4: { ID: 0x4008, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    pressure: { ID: 0x4009, type: zigbee_herdsman_1.Zcl.DataType.UINT16 }, // Not yet confirmed
                    unknown6: { ID: 0x400a, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown7: { ID: 0x400b, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                    unknown8: { ID: 0x400c, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
                },
                commands: {},
                commandsResponse: {},
            }),
            (0, modernExtend_1.deviceAddCustomCluster)('twinguardOptions', {
                ID: 0xe004,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    unknown1: { ID: 0x4000, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8 }, // 0,1 ??? read during pairing
                    pre_alarm: { ID: 0x4001, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8 }, // 0,1 on/off
                },
                commands: {},
                commandsResponse: {},
            }),
            (0, modernExtend_1.deviceAddCustomCluster)('twinguardSetup', {
                ID: 0xe006,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    unknown1: { ID: 0x5003, type: zigbee_herdsman_1.Zcl.DataType.INT8 }, // perhaps signal strength? -7?
                    unknown2: { ID: 0x5004, type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, // ????
                    heartbeat: { ID: 0x5005, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8 }, // 0
                },
                commands: {
                    pairingCompleted: {
                        ID: 0x01,
                        parameters: [],
                    },
                },
                commandsResponse: {},
            }),
            (0, modernExtend_1.deviceAddCustomCluster)('twinguardAlarm', {
                ID: 0xe007,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    alarm_status: { ID: 0x5000, type: zigbee_herdsman_1.Zcl.DataType.BITMAP32 },
                },
                commands: {
                    burglarAlarm: {
                        ID: 0x01,
                        parameters: [
                            { name: 'data', type: zigbee_herdsman_1.Zcl.DataType.UINT8 }, // data:1 trips the siren data:0 should stop the siren
                        ],
                    },
                },
                commandsResponse: {},
            }),
            boschExtend.twinguard(),
        ],
        configure: async (device, coordinatorEndpoint) => {
            await reporting.bind(device.getEndpoint(7), coordinatorEndpoint, ['genPollCtrl']);
            await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genAlarms', 'twinguardSmokeDetector', 'twinguardOptions']);
            await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['twinguardMeasurements']);
            await reporting.bind(device.getEndpoint(12), coordinatorEndpoint, ['twinguardSetup', 'twinguardAlarm']);
            await device.getEndpoint(1).read('twinguardOptions', ['unknown1'], manufacturerOptions); // Needed for pairing
            await device.getEndpoint(12).command('twinguardSetup', 'pairingCompleted', manufacturerOptions); // Needed for pairing
            await device.getEndpoint(1).write('twinguardSmokeDetector', { sensitivity: 0x0002 }, manufacturerOptions); // Setting defaults
            await device.getEndpoint(1).write('twinguardOptions', { pre_alarm: 0x01 }, manufacturerOptions); // Setting defaults
            await device.getEndpoint(12).write('twinguardSetup', { heartbeat: 0x01 }, manufacturerOptions); // Setting defaults
            await device.getEndpoint(1).read('twinguardSmokeDetector', ['sensitivity'], manufacturerOptions);
            await device.getEndpoint(1).read('twinguardOptions', ['pre_alarm'], manufacturerOptions);
            await device.getEndpoint(12).read('twinguardSetup', ['heartbeat'], manufacturerOptions);
        },
    },
    {
        zigbeeModel: ['RFPR-ZB-SH-EU'],
        model: 'RFPR-ZB-SH-EU',
        vendor: 'Bosch',
        description: 'Wireless motion detector',
        fromZigbee: [fromZigbee_1.default.temperature, fromZigbee_1.default.battery, fromZigbee_1.default.ias_occupancy_alarm_1],
        toZigbee: [],
        meta: { battery: { voltageToPercentage: '3V_2500' } },
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg']);
            await reporting.temperature(endpoint);
            await reporting.batteryVoltage(endpoint);
        },
        exposes: [e.temperature(), e.battery(), e.occupancy(), e.battery_low(), e.tamper()],
    },
    {
        zigbeeModel: ['RBSH-SP-ZB-EU', 'RBSH-SP-ZB-FR', 'RBSH-SP-ZB-GB'],
        model: 'BSP-FZ2',
        vendor: 'Bosch',
        description: 'Plug compact EU',
        ota: ota.zigbeeOTA,
        fromZigbee: [fromZigbee_1.default.on_off, fromZigbee_1.default.power_on_behavior, fromZigbee_1.default.electrical_measurement, fromZigbee_1.default.metering],
        toZigbee: [toZigbee_1.default.on_off, toZigbee_1.default.power_on_behavior],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await endpoint.read('genOnOff', ['onOff', 'startUpOnOff']);
            await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
            await reporting.bind(endpoint, coordinatorEndpoint, ['seMetering']);
            await reporting.readMeteringMultiplierDivisor(endpoint);
            await reporting.currentSummDelivered(endpoint, { change: [0, 1] });
            await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement']);
            await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
            await reporting.activePower(endpoint);
        },
        exposes: [e.switch(), e.power_on_behavior(), e.power(), e.energy()],
        whiteLabel: [
            { vendor: 'Bosch', model: 'BSP-EZ2', description: 'Plug compact FR', fingerprint: [{ modelID: 'RBSH-SP-ZB-FR' }] },
            { vendor: 'Bosch', model: 'BSP-GZ2', description: 'Plug compact UK', fingerprint: [{ modelID: 'RBSH-SP-ZB-GB' }] },
        ],
    },
    {
        zigbeeModel: ['RBSH-SWD-ZB'],
        model: 'BSEN-C2',
        vendor: 'Bosch',
        description: 'Door/window contact II',
        exposes: [],
        fromZigbee: [],
        toZigbee: [],
        extend: [
            boschExtend.doorWindowContact(false),
            (0, modernExtend_1.battery)({
                percentage: true,
                lowStatus: true,
            }),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
            await endpoint.read('ssIasZone', ['zoneStatus']);
        },
    },
    {
        zigbeeModel: ['RBSH-SWDV-ZB'],
        model: 'BSEN-CV',
        vendor: 'Bosch',
        description: 'Door/window contact II plus',
        exposes: [],
        fromZigbee: [],
        toZigbee: [],
        extend: [
            boschExtend.doorWindowContact(true),
            (0, modernExtend_1.battery)({
                percentage: true,
                lowStatus: true,
            }),
            (0, modernExtend_1.bindCluster)({
                cluster: 'genPollCtrl',
                clusterType: 'input',
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
            await endpoint.read('ssIasZone', ['zoneStatus']);
        },
    },
    {
        zigbeeModel: ['RBSH-MMD-ZB-EU'],
        model: 'BMCT-DZ',
        vendor: 'Bosch',
        description: 'Phase-cut dimmer',
        ota: ota.zigbeeOTA,
        extend: [(0, modernExtend_1.identify)(), (0, modernExtend_1.light)({ configureReporting: true, effect: false })],
    },
    {
        zigbeeModel: ['RBSH-MMR-ZB-EU'],
        model: 'BMCT-RZ',
        vendor: 'Bosch',
        description: 'Relay, potential free',
        extend: [(0, modernExtend_1.onOff)({ powerOnBehavior: false })],
    },
    {
        zigbeeModel: ['RBSH-MMS-ZB-EU'],
        model: 'BMCT-SLZ',
        vendor: 'Bosch',
        description: 'Light/shutter control unit II',
        fromZigbee: [
            fromZigbee_1.default.on_off,
            fromZigbee_1.default.power_on_behavior,
            fromZigbee_1.default.cover_position_tilt,
        ],
        toZigbee: [
            toZigbee_1.default.power_on_behavior,
            toZigbee_1.default.cover_position_tilt,
        ],
        meta: {
            multiEndpoint: true,
        },
        endpoint: (device) => {
            return { 'left': 2, 'right': 3 };
        },
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('boschSpecific', {
                ID: 0xfca0,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {
                    deviceMode: { ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
                    switchType: { ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
                    calibrationOpeningTime: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
                    calibrationClosingTime: { ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
                    childLock: { ID: 0x0008, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
                    motorState: { ID: 0x0013, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
                },
                commands: {},
                commandsResponse: {},
            }),
            boschExtend.bmct(),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint1 = device.getEndpoint(1);
            await reporting.bind(endpoint1, coordinatorEndpoint, ['genIdentify', 'closuresWindowCovering', 'boschSpecific']);
            await reporting.currentPositionLiftPercentage(endpoint1);
            await endpoint1.read('boschSpecific', ['deviceMode', 'switchType',
                'calibrationOpeningTime', 'calibrationClosingTime', 'childLock', 'motorState']).catch((e) => { });
            const endpoint2 = device.getEndpoint(2);
            await endpoint2.read('boschSpecific', ['childLock']);
            await reporting.bind(endpoint2, coordinatorEndpoint, ['genIdentify', 'genOnOff']).catch((e) => { });
            await reporting.onOff(endpoint2);
            const endpoint3 = device.getEndpoint(3);
            await endpoint3.read('boschSpecific', ['childLock']).catch((e) => { });
            await reporting.bind(endpoint3, coordinatorEndpoint, ['genIdentify', 'genOnOff']);
            await reporting.onOff(endpoint3);
        },
        exposes: (device, options) => {
            const stateDeviceMode = {
                'light': 0x04,
                'shutter': 0x01,
                'disabled': 0x00,
            };
            const stateMotor = {
                'stopped': 0x00,
                'opening': 0x01,
                'closing': 0x02,
            };
            const stateSwitchType = {
                'button': 0x01,
                'button_key_change': 0x02,
                'rocker_switch': 0x03,
                'rocker_switch_key_change': 0x04,
            };
            const commonExposes = [
                e.enum('switch_type', ea.ALL, Object.keys(stateSwitchType))
                    .withDescription('Module controlled by a rocker switch or a button'),
                e.linkquality(),
            ];
            const lightExposes = [
                e.switch().withEndpoint('left'),
                e.switch().withEndpoint('right'),
                e.power_on_behavior().withEndpoint('right'),
                e.power_on_behavior().withEndpoint('left'),
                e.binary('child_lock', ea.ALL, 'ON', 'OFF').withEndpoint('left')
                    .withDescription('Enable/Disable child lock'),
                e.binary('child_lock', ea.ALL, 'ON', 'OFF').withEndpoint('right')
                    .withDescription('Enable/Disable child lock'),
            ];
            const coverExposes = [
                e.cover_position(),
                e.enum('motor_state', ea.STATE, Object.keys(stateMotor))
                    .withDescription('Shutter motor actual state '),
                e.binary('child_lock', ea.ALL, 'ON', 'OFF').withDescription('Enable/Disable child lock'),
                e.numeric('calibration', ea.ALL).withUnit('s').withEndpoint('closing_time')
                    .withDescription('Calibration closing time').withValueMin(1).withValueMax(90),
                e.numeric('calibration', ea.ALL).withUnit('s').withEndpoint('opening_time')
                    .withDescription('Calibration opening time').withValueMin(1).withValueMax(90),
            ];
            if (device) {
                const deviceModeKey = device.getEndpoint(1).getClusterAttributeValue('boschSpecific', 'deviceMode');
                const deviceMode = Object.keys(stateDeviceMode).find((key) => stateDeviceMode[key] === deviceModeKey);
                if (deviceMode === 'light') {
                    return [...commonExposes, ...lightExposes];
                }
                else if (deviceMode === 'shutter') {
                    return [...commonExposes, ...coverExposes];
                }
            }
            return [e.enum('device_mode', ea.ALL, Object.keys(stateDeviceMode)).withDescription('Device mode'),
                e.linkquality()];
        },
    },
    {
        zigbeeModel: ['RBSH-US4BTN-ZB-EU'],
        model: 'BHI-US',
        vendor: 'Bosch',
        description: 'Universal Switch II',
        ota: ota.zigbeeOTA,
        fromZigbee: [fzLocal.bhius_button_press, fzLocal.bhius_config, fromZigbee_1.default.battery],
        toZigbee: [tzLocal.bhius_config],
        exposes: [
            e.battery_low(),
            e.battery_voltage(),
            e.text('config_led_top_left_press', ea.ALL).withLabel('LED config (top left short press)')
                .withDescription(labelShortPress)
                .withCategory('config'),
            e.text('config_led_top_right_press', ea.ALL).withLabel('LED config (top right short press)')
                .withDescription(labelShortPress)
                .withCategory('config'),
            e.text('config_led_bottom_left_press', ea.ALL).withLabel('LED config (bottom left short press)')
                .withDescription(labelShortPress)
                .withCategory('config'),
            e.text('config_led_bottom_right_press', ea.ALL).withLabel('LED config (bottom right short press)')
                .withDescription(labelShortPress)
                .withCategory('config'),
            e.text('config_led_top_left_longpress', ea.ALL).withLabel('LED config (top left long press)')
                .withDescription(labelLongPress)
                .withCategory('config'),
            e.text('config_led_top_right_longpress', ea.ALL).withLabel('LED config (top right long press)')
                .withDescription(labelLongPress)
                .withCategory('config'),
            e.text('config_led_bottom_left_longpress', ea.ALL).withLabel('LED config (bottom left long press)')
                .withDescription(labelLongPress)
                .withCategory('config'),
            e.text('config_led_bottom_right_longpress', ea.ALL).withLabel('LED config (bottom right long press)')
                .withDescription(labelLongPress)
                .withCategory('config'),
        ],
        extend: [
            (0, modernExtend_1.deviceAddCustomCluster)('boschSpecific', {
                ID: 0xfca1,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
                attributes: {},
                commands: {
                    confirmButtonPressed: {
                        ID: 0x0010,
                        parameters: [
                            { name: 'data', type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER },
                        ],
                    },
                    pairingCompleted: {
                        ID: 0x0012,
                        parameters: [
                            { name: 'data', type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER },
                        ],
                    },
                },
                commandsResponse: {},
            }),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const endpoint = device.getEndpoint(1);
            // Read default LED configuration
            await endpoint.read('boschSpecific', [0x0010, 0x0011, 0x0012, 0x0013], { ...manufacturerOptions, sendPolicy: 'immediate' })
                .catch((error) => { });
            await endpoint.read('boschSpecific', [0x0020, 0x0021, 0x0022, 0x0023], { ...manufacturerOptions, sendPolicy: 'immediate' })
                .catch((error) => { });
            // We also have to read this one. Value reads 0x0f, looks like a bitmap
            await endpoint.read('boschSpecific', [0x0024], { ...manufacturerOptions, sendPolicy: 'immediate' });
            await endpoint.command('boschSpecific', 'pairingCompleted', { data: Buffer.from([0x00]) }, { sendPolicy: 'immediate' });
            await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'genBasic', 'boschSpecific']);
            await reporting.batteryPercentageRemaining(endpoint);
            await reporting.batteryVoltage(endpoint);
        },
    },
];
exports.default = definitions;
module.exports = definitions;
//# sourceMappingURL=bosch.js.map