Skip to content

Instantly share code, notes, and snippets.

@Koenkk
Last active December 8, 2022 17:30
Show Gist options
  • Save Koenkk/2835843081f821e0d01d5a892c5be3cf to your computer and use it in GitHub Desktop.
Save Koenkk/2835843081f821e0d01d5a892c5be3cf to your computer and use it in GitHub Desktop.
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const fzLocal = {
ZMCSW032D_cover_position: {
cluster: 'closuresWindowCovering',
type: ['attributeReport', 'readResponse'],
options: [
exposes.options.invert_cover(),
exposes.numeric('time_close')
.withDescription(`Set the full closing time of the roller shutter (e.g. set it to 20) (value is in s).`),
exposes.numeric('time_open')
.withDescription(`Set the full opening time of the roller shutter (e.g. set it to 21) (value is in s).`),
],
convert: (model, msg, publish, options, meta) => {
const result = {};
const timeCoverSetMiddle = 60;
// https://github.com/Koenkk/zigbee-herdsman-converters/pull/1336
// Need to add time_close and time_open in your configuration.yaml after friendly_name (and set your time)
if (options.hasOwnProperty('time_close') && options.hasOwnProperty('time_open')) {
if (!globalStore.hasValue(msg.endpoint, 'position')) {
globalStore.putValue(msg.endpoint, 'position', {lastPreviousAction: -1, CurrentPosition: -1, since: false});
}
const entry = globalStore.getValue(msg.endpoint, 'position');
// ignore if first action is middle and ignore action middle if previous action is middle
if (msg.data.hasOwnProperty('currentPositionLiftPercentage') && msg.data['currentPositionLiftPercentage'] == 50 ) {
if ((entry.CurrentPosition == -1 && entry.lastPreviousAction == -1) ||
entry.lastPreviousAction == 50 ) {
meta.logger.warn(`ZMCSW032D ignore action `);
return;
}
}
let currentPosition = entry.CurrentPosition;
const lastPreviousAction = entry.lastPreviousAction;
const deltaTimeSec = Math.floor((Date.now() - entry.since)/1000); // convert to sec
entry.since = Date.now();
entry.lastPreviousAction = msg.data['currentPositionLiftPercentage'];
if (msg.data.hasOwnProperty('currentPositionLiftPercentage') && msg.data['currentPositionLiftPercentage'] == 50 ) {
if (deltaTimeSec < timeCoverSetMiddle || deltaTimeSec > timeCoverSetMiddle) {
if (lastPreviousAction == 100 ) {
// Open
currentPosition = currentPosition == -1 ? 0 : currentPosition;
currentPosition = currentPosition + ((deltaTimeSec * 100)/options.time_open);
} else if (lastPreviousAction == 0 ) {
// Close
currentPosition = currentPosition == -1 ? 100 : currentPosition;
currentPosition = currentPosition - ((deltaTimeSec * 100)/options.time_close);
}
currentPosition = currentPosition > 100 ? 100 : currentPosition;
currentPosition = currentPosition < 0 ? 0 : currentPosition;
}
}
entry.CurrentPosition = currentPosition;
if (msg.data.hasOwnProperty('currentPositionLiftPercentage') && msg.data['currentPositionLiftPercentage'] !== 50 ) {
// postion cast float to int
result.position = currentPosition | 0;
} else {
if (deltaTimeSec < timeCoverSetMiddle || deltaTimeSec > timeCoverSetMiddle) {
// postion cast float to int
result.position = currentPosition | 0;
} else {
entry.CurrentPosition = lastPreviousAction;
result.position = lastPreviousAction;
}
}
result.position = options.invert_cover ? 100 - result.position : result.position;
} else {
// Previous solution without time_close and time_open
if (msg.data.hasOwnProperty('currentPositionLiftPercentage') && msg.data['currentPositionLiftPercentage'] !== 50) {
const liftPercentage = msg.data['currentPositionLiftPercentage'];
result.position = liftPercentage;
result.position = options.invert_cover ? 100 - result.position : result.position;
}
}
// Add the state
if ('position' in result) {
result.state = result.position === 0 ? 'CLOSE' : 'OPEN';
}
return result;
},
}
}
const definition = {
zigbeeModel: ['TS0302'],
model: 'ZM-CSW032-D',
vendor: 'Zemismart',
description: 'Curtain/roller blind switch',
fromZigbee: [fz.ignore_basic_report, fzLocal.ZMCSW032D_cover_position],
toZigbee: [tz.cover_state, tz.ZMCSW032D_cover_position],
exposes: [e.cover_position()],
meta: {multiEndpoint: true},
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(1);
await reporting.bind(endpoint, coordinatorEndpoint, ['closuresWindowCovering']);
// Configure reporing of currentPositionLiftPercentage always fails.
// https://github.com/Koenkk/zigbee2mqtt/issues/3216
},
};
module.exports = definition;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment