Skip to content

Commit

Permalink
Add CCTFR6400 (#2558)
Browse files Browse the repository at this point in the history
* Add CCTFR6700

* Add schneider_pilot_mode

* Add Schneider Electric CCTFR6700

* File already exists

* Add schneider_pilot_mode to CCTFR6700

* Update toZigbee.js

* Added fromZigbee for schneider_pilot_mode

* Udpated toZigbee for schneider_pilot_mode

* Added fromZigbee pilot mode and fixed expose for energy/power

* Removed configureKey

* Update schneider_electric.js

* Added measured temperature SET for CCTFR6700

* Added measured temperature SET for CCTFR6700

Requires 'report' capability merged into herdsman.

* Added push/pop to global store

Used for deferred writes

* Added toZigbee for CCTFR6400

* Added CCTFR6400 thermostat

* Added from zigbee for CCTFR6400

Also forced local_temperature othewise Home assistant is not happy

* Improved CCTFR6400 and 6700

* Added UI temperature update of CCTFR6400

* Updated using ClusterAttributeValue

* Updated using ClusterAttributeValue

* Updated using ClusterAttributeValue

* Removed legacy support for schneider_thermostat_system_mode

* Removed temperature expose

Temperature is exposed as local_temperature only

* CCTFR6400 tempreature exposed as local_temperature

* Used sendWhenActive for keypadLockout

* Used sendWhenActive for keypadLockout and added defaults

* Fixed schneider_ui_action convert

* Simplified schneider_thermostat_control_sequence_of_operation

* Reverted Push/pop value

* Update toZigbee.js

* Update toZigbee.js

* Update schneider_electric.js

* Update fromZigbee.js

* Update schneider_electric.js

* Update schneider_electric.js

* Fix'

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
  • Loading branch information
H3buss and Koenkk committed May 22, 2021
1 parent f0a05c5 commit d0fb06c
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 3 deletions.
48 changes: 48 additions & 0 deletions converters/fromZigbee.js
Expand Up @@ -18,6 +18,7 @@ const tuya = require('../lib/tuya');
const globalStore = require('../lib/store');
const constants = require('../lib/constants');
const libColor = require('../lib/color');
const utils = require('../lib/utils');

const converters = {
// #region Generic/recommended converters
Expand Down Expand Up @@ -5570,6 +5571,53 @@ const converters = {
return result;
},
},
schneider_ui_action: {
cluster: 'wiserDeviceInfo',
type: 'attributeReport',
convert: (model, msg, publish, options, meta) => {
if (hasAlreadyProcessedMessage(msg)) return;

const data = msg.data['deviceInfo'].split(',');
if (data[0] === 'UI' && data[1]) {
const result = {action: utils.toSnakeCase(data[1])};

let screenAwake = globalStore.getValue(msg.endpoint, 'screenAwake');
screenAwake = screenAwake != undefined ? screenAwake : false;
let keypadLocked = msg.endpoint.getClusterAttributeValue('hvacUserInterfaceCfg', 'keypadLockout');
keypadLocked = keypadLocked != undefined ? keypadLocked != 0 : false;

// Emulate UI temperature update
if (data[1] === 'ScreenWake') {
globalStore.putValue(msg.endpoint, 'screenAwake', true);
} else if (data[1] === 'ScreenSleep') {
globalStore.putValue(msg.endpoint, 'screenAwake', false);
} else if (screenAwake && !keypadLocked) {
let occupiedHeatingSetpoint = msg.endpoint.getClusterAttributeValue('hvacThermostat', 'occupiedHeatingSetpoint');
occupiedHeatingSetpoint = occupiedHeatingSetpoint != null ? occupiedHeatingSetpoint : 400;

if (data[1] === 'ButtonPressMinusDown') {
occupiedHeatingSetpoint -= 50;
} else if (data[1] === 'ButtonPressPlusDown') {
occupiedHeatingSetpoint += 50;
}

msg.endpoint.saveClusterAttributeKeyValue('hvacThermostat', {occupiedHeatingSetpoint: occupiedHeatingSetpoint});
result.occupied_heating_setpoint = occupiedHeatingSetpoint/100;
}

return result;
}
},
},
schneider_temperature: {
cluster: 'msTemperatureMeasurement',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
const temperature = parseFloat(msg.data['measuredValue']) / 100.0;
const property = postfixWithEndpointName('local_temperature', msg, model);
return {[property]: calibrateAndPrecisionRoundOptions(temperature, options, 'temperature')};
},
},

// #endregion

Expand Down
46 changes: 46 additions & 0 deletions converters/toZigbee.js
Expand Up @@ -4886,6 +4886,52 @@ const converters = {
await entity.read('schneiderSpecificPilotMode', ['pilotMode'], {manufacturerCode: 0x105e});
},
},
schneider_temperature_measured_value: {
key: ['temperature_measured_value'],
convertSet: async (entity, key, value, meta) => {
await entity.report('msTemperatureMeasurement', {'measuredValue': Math.round(value*100)});
},
},
schneider_thermostat_system_mode: {
key: ['system_mode'],
convertSet: async (entity, key, value, meta) => {
const systemMode = utils.getKey(constants.thermostatSystemModes, value, undefined, Number);
entity.saveClusterAttributeKeyValue('hvacThermostat', {systemMode: systemMode});
return {state: {system_mode: value}};
},
},
schneider_thermostat_occupied_heating_setpoint: {
key: ['occupied_heating_setpoint'],
convertSet: async (entity, key, value, meta) => {
const occupiedHeatingSetpoint = (Math.round((value * 2).toFixed(1)) / 2).toFixed(1) * 100;
entity.saveClusterAttributeKeyValue('hvacThermostat', {occupiedHeatingSetpoint: occupiedHeatingSetpoint});
return {state: {occupied_heating_setpoint: value}};
},
},
schneider_thermostat_control_sequence_of_operation: {
key: ['control_sequence_of_operation'],
convertSet: async (entity, key, value, meta) => {
const val = utils.getKey(constants.thermostatControlSequenceOfOperations, value, value, Number);
entity.saveClusterAttributeKeyValue('hvacThermostat', {ctrlSeqeOfOper: val});
return {state: {control_sequence_of_operation: value}};
},
},
schneider_thermostat_pi_heating_demand: {
key: ['pi_heating_demand'],
convertSet: async (entity, key, value, meta) => {
entity.saveClusterAttributeKeyValue('hvacThermostat', {pIHeatingDemand: value});
return {state: {pi_heating_demand: value}};
},
},
schneider_thermostat_keypad_lockout: {
key: ['keypad_lockout'],
convertSet: async (entity, key, value, meta) => {
const keypadLockout = utils.getKey(constants.keypadLockoutMode, value, value, Number);
entity.write('hvacUserInterfaceCfg', {keypadLockout}, {sendWhenActive: true});
entity.saveClusterAttributeKeyValue('hvacUserInterfaceCfg', {keypadLockout});
return {state: {keypad_lockout: value}};
},
},
ZNCJMB14LM: {
key: ['theme',
'standby_enabled',
Expand Down
34 changes: 31 additions & 3 deletions devices/schneider_electric.js
Expand Up @@ -197,10 +197,12 @@ module.exports = [
vendor: 'Schneider Electric',
description: 'Heating thermostat',
fromZigbee: [fz.thermostat, fz.metering, fz.schneider_pilot_mode],
toZigbee: [tz.thermostat_system_mode, tz.thermostat_running_state, tz.thermostat_local_temperature,
tz.thermostat_occupied_heating_setpoint, tz.thermostat_control_sequence_of_operation, tz.schneider_pilot_mode],
toZigbee: [tz.schneider_temperature_measured_value, tz.thermostat_system_mode, tz.thermostat_running_state,
tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_control_sequence_of_operation,
tz.schneider_pilot_mode, tz.schneider_temperature_measured_value],
exposes: [e.power(), e.energy(),
exposes.enum('schneider_pilot_mode', ea.ALL, ['relay', 'pilot']).withDescription('Controls piloting mode'),
exposes.enum('schneider_pilot_mode', ea.ALL, ['contactor', 'pilot']).withDescription('Controls piloting mode'),
exposes.numeric('temperature_measured_value', ea.SET),
exposes.climate().withSetpoint('occupied_heating_setpoint', 4, 30, 0.5).withLocalTemperature()
.withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat']).withPiHeatingDemand()],
configure: async (device, coordinatorEndpoint, logger) => {
Expand All @@ -214,4 +216,30 @@ module.exports = [
await reporting.currentSummDelivered(endpoint2, {min: 0, max: 60, change: 1});
},
},
{
fingerprint: [{modelID: 'Thermostat', manufacturerName: 'Schneider Electric'}],
model: 'CCTFR6400',
vendor: 'Schneider Electric',
description: 'Temperature/Humidity measurement with thermostat interface',
fromZigbee: [fz.battery, fz.schneider_temperature, fz.humidity, fz.thermostat, fz.schneider_ui_action],
toZigbee: [tz.schneider_thermostat_system_mode, tz.schneider_thermostat_occupied_heating_setpoint,
tz.schneider_thermostat_control_sequence_of_operation, tz.schneider_thermostat_pi_heating_demand,
tz.schneider_thermostat_keypad_lockout],
exposes: [e.keypad_lockout().withAccess(ea.STATE_SET), e.humidity(), e.battery(), e.battery_voltage(),
e.action(['screen_sleep', 'screen_wake', 'button_press_plus_down', 'button_press_center_down', 'button_press_minus_down']),
exposes.climate().withSetpoint('occupied_heating_setpoint', 4, 30, 0.5, ea.SET).withLocalTemperature(ea.STATE)
.withPiHeatingDemand(ea.SET)],
meta: {battery: {dontDividePercentage: true}},
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint1 = device.getEndpoint(1);
await reporting.bind(endpoint1, coordinatorEndpoint,
['genPowerCfg', 'hvacThermostat', 'msTemperatureMeasurement', 'msRelativeHumidity']);
await reporting.temperature(endpoint1);
await reporting.humidity(endpoint1);
await reporting.batteryPercentageRemaining(endpoint1);
endpoint1.saveClusterAttributeKeyValue('genBasic', {zclVersion: 3});
endpoint1.saveClusterAttributeKeyValue('hvacThermostat', {schneiderWiserSpecific: 1, systemMode: 4, ctrlSeqeOfOper: 2});
endpoint1.saveClusterAttributeKeyValue('hvacUserInterfaceCfg', {keypadLockout: 0});
},
},
];

12 comments on commit d0fb06c

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c May 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to work, a hacked firmware is needed so that read to hvac cluster on enpoint 3 are seen by the coordinator.
Do you plan to publish that firmware?

@Koenkk
Copy link
Owner

@Koenkk Koenkk commented on d0fb06c May 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@H3buss yes, I will include these changes in the next fw updates. (cc2652/cc1352 will come first)

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c May 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, that's the dongle I use in production env.
I'll update the doc, maybe tomorrow

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c Jun 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to come back on that issue, but I installed my production environnement and the the thermostat no longer take the readResponses into account. I think I messed-up when I tested with the hacked firmware a while ago.

After some troubleshooting, the readResponses are sent from coordinator endpoint 3, and the sent attributes are correct, however the profile is incorrect: should be 0x0104 for Home Automation, and is 0x0105.

How can I set the profile for the read responses?

@Koenkk
Copy link
Owner

@Koenkk Koenkk commented on d0fb06c Jun 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a bit troublesome because the profileID cannot be send via the API. I think the firmware uses the profileID of the endpoint, but changing the profileID is not possible since this could potentially break other devices.

We can try to set the profile id to the wildcard one 0xFFFF. Could you try to set that, reflash your stick and see what happens?

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with a CC2652R (offcial firmware) and modified to 0xFFFF the profile of endpoint 3 https://github.com/Koenkk/zigbee-herdsman/blob/fbe8b70e7ec7f540b0749adcd82922564f858ef2/src/adapter/z-stack/adapter/endpoints.ts#L25

Unfortunately, the read on endpoint 3 profile 0x0105 are not forwarded to herdsman. Should I try with the hacked firware on CC2531? (this is my production adapter, so I need to migrate everything before testing)

@Koenkk
Copy link
Owner

@Koenkk Koenkk commented on d0fb06c Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you will need the hacker cc2531 firmware, I will update the CC2652R with this hack later.

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with profile 0xFFFF.
The coordinator does respond with 0xFFFF as profile id, but unfortunately the response is ignored by the device.

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c Jun 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So maybe the only option would be a configuration option in the main conf that makes the adapter compatible with that device, loosing compatibility with other devices along the way.

@Koenkk
Copy link
Owner

@Koenkk Koenkk commented on d0fb06c Jun 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@H3buss yes, but this also would require a reset of your network, I need to think of an acceptable solution.

@H3buss
Copy link
Contributor Author

@H3buss H3buss commented on d0fb06c Jun 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Koenkk Hello! Sorry to bother you on this issue. Did you think of a solution yet?

@Koenkk
Copy link
Owner

@Koenkk Koenkk commented on d0fb06c Jun 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@H3buss I've checked the api, but the profileID is set in the closed source part of the firmware. I've changed the profileID in zigbee-herdsman now, fingers crossed that it doesn't introduce any other problems :)

Please sign in to comment.