'use strict';
const { ZigBeeDevice } = require("homey-zigbeedriver");
const { CLUSTER, Cluster } = require('zigbee-clusters')
const appkit = require('./lib/');
const destructConstProps = function({
ID, NAME, ATTRIBUTES, COMMANDS,
}) {
return Object.freeze({
ID, NAME, ATTRIBUTES, COMMANDS,
});
}
const HzcThermostatCluster = require('../../lib/HzcThermostatCluster')
const HzcThermostatUserInterfaceConfigurationCluster =
require('../../lib/HzcThermostatUserInterfaceConfigurationCluster')
Cluster.addCluster(HzcThermostatCluster)
Cluster.addCluster(HzcThermostatUserInterfaceConfigurationCluster)
CLUSTER['THERMOSTAT_USER_INTERFACE_CONFIGURATION'] =
destructConstProps(HzcThermostatUserInterfaceConfigurationCluster)
const getInt16 = function(number) {
const int16 = new Int16Array(1)
int16[0] = number
return int16[0]
}
const {
getOptBaseTime, TIP_CHANGED
} = require('./lib/devices/utils');
class thermostat_t7e_zg_thermostat extends ZigBeeDevice {
onEndDeviceAnnounce(){
this.log('@@@thermostat_t7e_zg_thermostat.onEndDeviceAnnounce')
}
async onNodeInit ({ zclNode, node }) {
super.onNodeInit({ zclNode: zclNode, node: node })
this.enableDebug();
//this.printNode();
this._start()
};
async _start(){
this._thermostatCluster().
readAttributes(['sensorMode']).
then(value => {
this.log(`++++++ thermostat sensorMode = `, value)
if (value.hasOwnProperty('sensorMode')){
let sensorMode = value['sensorMode'] || 'a'
if (sensorMode === 'p'){
this.setStoreValue('regulator_mode', "1")
}
else {
this.setStoreValue('regulator_mode', "0")
}
this._restartApp();
}
}).
catch(err => {
this.error('Error: App read sensor mode failed', err);
this.setUnavailable('App read sensor mode failed, Please try again.')
this.setStoreValue('zb_first_init', true)
setTimeout(() => {
this._start()
}, 3000)
})
}
async _restartApp(){
//this.onDeleted()
//super.onDeleted()
this.setUnavailable('Device initializing ...')
this.showMessage(TIP_CHANGED)
try{
let reg_mode = this.getStoreValue('regulator_mode') || '0';
console.log('restartApp->w:', reg_mode);
if (!this.hasCapability('onoff')){ await this.addCapability("onoff"); }
if (!this.hasCapability('measure_power')){ await this.addCapability("measure_power"); }
if (!this.hasCapability('meter_power')) { await this.addCapability("meter_power"); }
if (!this.hasCapability('t7e_zg_sensor_mode')){ await this.addCapability('t7e_zg_sensor_mode') }
if (!this.hasCapability('t7e_zg_window_state')) { await this.addCapability('t7e_zg_window_state'); }
if (!this.hasCapability('t7e_zg_fault')) { await this.addCapability('t7e_zg_fault') }
if (!this.hasCapability('child_lock')) { await this.addCapability("child_lock"); }
if (reg_mode === '1') {
//remove
if (this.hasCapability('target_temperature')) { await this.removeCapability('target_temperature') }
if (this.hasCapability('measure_temperature')) { await this.removeCapability('measure_temperature') }
if (this.hasCapability('t7e_zg_thermostat_mode')){ await this.removeCapability('t7e_zg_thermostat_mode') }
if (this.hasCapability('eco_mode')){ await this.removeCapability('eco_mode') }
if (this.hasCapability('frost')){ await this.removeCapability('frost') }
//add
if (!this.hasCapability('t7e_zg_regulator_percentage')){ await this.addCapability('t7e_zg_regulator_percentage'); }
this.setSettings({
sensor_mode: 'p',
thermostat_regulator_mode: '1',
});
this.setCapabilityValue('t7e_zg_sensor_mode', 'p')
let rp = this.getStoreValue('t7e_zg_regulator_percentage') || 20;
this.setCapabilityValue('t7e_zg_regulator_percentage', rp);
}
else{
//remove
if (this.hasCapability('t7e_zg_regulator_percentage')){ await this.removeCapability('t7e_zg_regulator_percentage') }
//add
if (!this.hasCapability('t7e_zg_thermostat_mode')){ await this.addCapability('t7e_zg_thermostat_mode'); }
if (!this.hasCapability('target_temperature')){ await this.addCapability("target_temperature"); }
if (!this.hasCapability('measure_temperature')){ await this.addCapability("measure_temperature"); }
if (!this.hasCapability('eco_mode')){ await this.addCapability("eco_mode"); }
if (!this.hasCapability('frost')){ await this.addCapability("frost"); }
let settings = this.getSettings();
let mode1 = settings.sensor_mode;
if (mode1 === 'p'){
mode1 = 'a';
this.setSettings({
sensor_mode: mode1,
});
}
this.setCapabilityValue('t7e_zg_sensor_mode', mode1)
this.setSettings({
thermostat_regulator_mode: '0',
});
}
}catch(err){
console.log('restartApp ERROR', err);
}
this._initCapabilityAndListener();
}
_initCapabilityAndListener(){
this._setUpSystemCapabilities().catch(this.error)
this._setUpMeasureTemperatureCapability()
this._setUpTargetTemperatureCapability()
this._setUpModesCapability()
this._setUpWindowOpenFlagCapability()
appkit.regulator_percentage.init(this)
appkit.eco_mode.init(this)
appkit.child_lock.init(this)
appkit.frost.init(this)
appkit.sensor_mode.init(this)
appkit.fault.init(this)
this._getAttributes();
this.setStoreValue('regulator_mode_changed', false);
this.log('+++++ setAvailable AND ready ')
this.setAvailable()
this._onHandlerReport()
}
async showMessage(msg){
await this.setWarning(msg);
await this.unsetWarning();
}
//设备状态上报通知
onStatusChangeNotificationReport(payload){
this.log('@@@ onStatusChangeNotificationReport ', payload)
}
async _onHandlerReport(){
// onStatusChangeNotification
this.zclNode.endpoints[1].clusters[CLUSTER.THERMOSTAT.NAME]
.onStatusChangeNotification = payload => {
this.onStatusChangeNotificationReport(payload);
}
return;
this.configureAttributeReporting([
{
cluster: CLUSTER.THERMOSTAT,
attributeName: 'localTemperature',
minInterval: 0,
maxInterval: 300,
minChange: 1
},
{
cluster: CLUSTER.THERMOSTAT,
attributeName: 'occupiedHeatingSetpoint',
minInterval: 0,
maxInterval: 300,
minChange: 1
},
{
cluster: CLUSTER.THERMOSTAT_USER_INTERFACE_CONFIGURATION,
attributeName: 'keypadLockout',
minInterval: 0,
maxInterval: 300,
minChange: 1
}
])
this.zclNode.endpoints[1].clusters[CLUSTER.THERMOSTAT.NAME].on('attr.localTemperature', this.onLocalTemperatureReport.bind(this))
this.zclNode.endpoints[1].clusters[CLUSTER.THERMOSTAT.NAME].on('attr.occupiedHeatingSetpoint', this.onOccupiedHeatingSetpointReport.bind(this))
this.zclNode.endpoints[1].clusters['thermostatUserInterfaceConfiguration'].on('attr.keypadLockout', this.onKeypadLockoutReport.bind(this))
}
onOccupiedHeatingSetpointReport(value) {
this.log(`@@@ onOccupiedHeatingSetpointReport `, value)
const temp = parseFloat(
(value['occupiedHeatingSetpoint'] / 100).toFixed(1))
return this.setCapabilityValue('target_temperature', temp)
}
onLocalTemperatureReport(value) {
this.log('@@@ onLocalTemperatureReport ', value)
}
onKeypadLockoutReport(value){
this.log('@@@ onKeypadLockoutReport ', value)
}
_thermostatCluster() { return this.zclNode.endpoints[1].clusters.thermostat }
//child lock
_thermostatUserInterfaceConfiguration () {
return this.zclNode.endpoints[1].clusters.thermostatUserInterfaceConfiguration
}
async _setUpSystemCapabilities () {
// onoff
this.registerCapabilityListener('onoff', async isOn => {
this.log('==========onoff: ', isOn)
let regModeChanged = this.getStoreValue('regulator_mode_changed') || false
if (regModeChanged){
//this._start()
//return;
}
let lastSystemMode = this.getStoreValue('last_system_mode') || 'heat'
this.log('.......onoff set ', lastSystemMode)
this._thermostatCluster().writeAttributes({ systemMode: isOn ? lastSystemMode : 'off', })
})
// meter_power
if (this.hasCapability('meter_power')) {
const {
multiplier, divisor,
} = await this.zclNode.endpoints[this.getClusterEndpoint(
CLUSTER.METERING)].clusters[CLUSTER.METERING.NAME].readAttributes(
'multiplier', 'divisor').catch(this.error)
this.log('multiplier-divisor ', multiplier, divisor)
let md = 0.1;
if (multiplier && divisor){
md = multiplier / divisor;
}
this.registerCapability('meter_power', CLUSTER.METERING, {
get: 'currentSummationDelivered',
report: 'currentSummationDelivered',
reportParser: value => {
this.log(`currentSummationDelivered `, value)
return value * md
},
getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
},
reportOpts: {
configureAttributeReporting: {
minInterval: 10,
maxInterval: 60000,
minChange: 10,
},
},
})
}
// measure_power
if (this.hasCapability('measure_power')) {
this.registerCapability('measure_power', CLUSTER.ELECTRICAL_MEASUREMENT, {
get: 'activePower', report: 'activePower', reportParser: value => {
return value / 10
}, getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
}, reportOpts: {
configureAttributeReporting: {
minInterval: 10,
maxInterval: 60000,
minChange: 10,
},
},
})
}
}
_setUpMeasureTemperatureCapability () {
if (!this.hasCapability('measure_temperature')) return
this.registerCapability('measure_temperature', CLUSTER.THERMOSTAT, {
get: 'localTemperature',
report: 'localTemperature',
reportParser: value => {
let temp = parseFloat((getInt16(value) / 100).toFixed(1))
this.log(`localTemperature report `, value, temp)
if (temp < -10) temp = -10
if (temp > 60) temp = 60
return temp
},
getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
getOnOnline: true
},
reportOpts: {
configureAttributeReporting: {
minInterval: 0,
maxInterval: 600,
minChange: 5,
},
},
})
}
_setUpTargetTemperatureCapability () {
if (!this.hasCapability('target_temperature')) return
this.registerCapability('target_temperature', CLUSTER.THERMOSTAT, {
get: 'occupiedHeatingSetpoint',
report: 'occupiedHeatingSetpoint',
reportParser: value => {
if (this.getCapabilityValue('t7e_zg_thermostat_mode') === 'off') {
this._thermostatCluster().
readAttributes('systemMode').
then(value => {
let mode = value.systemMode
let isOn = mode !== 'off'
this.setCapabilityValue('onoff', isOn).catch(this.error)
this.setCapabilityValue('t7e_zg_thermostat_mode', mode).catch(this.error)
this.log(`systemMode after occupiedHeatingSetpoint `, value, mode,
isOn)
}).
catch(this.error)
}
let temp = parseFloat((getInt16(value) / 100).toFixed(1))
this.log(`occupiedHeatingSetpoint report `, value)
return temp
},
getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
},
reportOpts: {
configureAttributeReporting: {
minInterval: 0,
maxInterval: 600,
minChange: 5,
},
},
})
this.registerCapabilityListener('target_temperature', async value => {
this.log(`occupiedHeatingSetpoint setParser `, value)
let payload = {
occupiedHeatingSetpoint: value * 100,
}
return this._thermostatCluster().writeAttributes(payload)
})
}
_setUpWindowOpenFlagCapability() {
if (!this.hasCapability('t7e_zg_window_state')) return
this.registerCapability('t7e_zg_window_state', CLUSTER.THERMOSTAT, {
get: 'windowState', report: 'windowState', reportParser: value => {
this.log(`windowState report `, value, value === 'opened')
return value === 'opened' ? true : false
},
getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
getOnOnline: true,
},
reportOpts: {
configureAttributeReporting: {
minInterval: 10,
maxInterval: 600,
minChange: 1,
},
},
})
}
_setUpModesCapability() {
if (!this.hasCapability('t7e_zg_thermostat_mode')) return
this.registerCapability('t7e_zg_thermostat_mode', CLUSTER.THERMOSTAT, {
get: 'systemMode',
getOpts: {
getOnStart: true, getOnOnline: true, pollInterval: getOptBaseTime,
},
report: 'systemMode',
reportParser: value => {
// Refresh onoff
let isOn = value != 'off'
this.setCapabilityValue('onoff', isOn).catch(this.error)
if (isOn === false) {
this.setCapabilityValue('target_temperature', 5)
} else {
// Refresh heating setpoint
this._thermostatCluster().
readAttributes('occupiedHeatingSetpoint').
then(value => {
this.log(`occupiedHeatingSetpoint after mode `, value)
const temp = parseFloat(
(value['occupiedHeatingSetpoint'] / 100).toFixed(1))
return this.setCapabilityValue('target_temperature', temp)
}).
catch(this.error)
}
this.log(`systemMode report `, value)
return value
},
getOpts: {
getOnStart: true, pollInterval: getOptBaseTime,
getOnOnline: true,
},
reportOpts: {
configureAttributeReporting: {
minInterval: 10,
maxInterval: 600,
minChange: 1,
},
},
})
this.registerCapabilityListener('t7e_zg_thermostat_mode', async value => {
this.log(`systemMode set `, value)
let payload = {
systemMode: value,
}
this.setStoreValue('last_system_mode', value);
return this._thermostatCluster().writeAttributes(payload)
})
}
async onSettings({ oldSettings, newSettings, changedKeys }) {
// run when the user has changed the device's settings in Homey.
// changedKeysArr contains an array of keys that have been changed
// if the settings must not be saved for whatever reason:
// throw new Error('Your error message');
this._setDeviceSettings(newSettings, changedKeys);
}
async _setDeviceSettings (newSettings, changedKeys) {
this.log('+++++ settings : ', newSettings, changedKeys);
changedKeys.forEach(element => {
console.log("");console.log("");console.log("");
console.log("-----------------------config:", element);
let o = appkit[element];
if (o != undefined){
if (o['setConfig']){
o.setConfig(this, newSettings[element]);
}
}
})
}
_getAttributes () {
this.log('+++++++++++++++++++ Refresh state from device.')
this._thermostatUserInterfaceConfiguration().
readAttributes(['keypadLockout']).
then(value => {
this.log(`+++++++ child lock = `, value)
if (value.hasOwnProperty('keypadLockout')){
let isOpen = value['keypadLockout'] === 'level1Lockout'
this.setCapabilityValue('child_lock', isOpen ? true : false)
}
}).
catch(this.error)
this._thermostatCluster().
readAttributes(['windowState']).
then(value => {
this.log(`++++++ thermostat windowState = `, value)
if (value.hasOwnProperty('windowState')) {
let isOpen = value['windowState'] === 'opened'
this.setCapabilityValue('t7e_zg_window_state', isOpen ? true : false)
}
}).
catch(this.error)
// target_temperature
if (this.hasCapability('target_temperature')){
this._thermostatCluster().
readAttributes('occupiedHeatingSetpoint').
then(value => {
this.log(`+++++++ occupiedHeatingSetpoint after mode `, value)
const temp = parseFloat(
(value['occupiedHeatingSetpoint'] / 100).toFixed(1))
return this.setCapabilityValue('target_temperature', temp)
}).
catch(this.error)
}
//measure_temperature
if (this.hasCapability('measure_temperature')){
this._thermostatCluster().
readAttributes('localTemperature').
then(value => {
this.log(`++++++++++ localTemperature`, value)
const temp = parseFloat(
(value['localTemperature'] / 100).toFixed(1))
return this.setCapabilityValue('measure_temperature', temp)
}).
catch(this.error)
}
if (this.hasCapability('frost')){
this._thermostatCluster().
readAttributes(['frost']).
then(value => {
this.log(`++++++ thermostat frost = `, value)
if (value.hasOwnProperty('frost')){
let isOpen = value['frost'] === 'opened'
this.setCapabilityValue('frost', isOpen ? true : false)
}
}).
catch(this.error)
}
//t7e_zg_regulator_percentage
if (this.hasCapability('t7e_zg_regulator_percentage')){
this._thermostatCluster().
readAttributes(['pIHeatingDemand']).
then(value => {
this.log(`++++++ thermostat pIHeatingDemand = `, value)
if (value.hasOwnProperty('pIHeatingDemand')){
this.setCapabilityValue('t7e_zg_regulator_percentage', value['pIHeatingDemand'])
}
}).
catch(this.error)
}
//lcd backlight
this._thermostatCluster().readAttributes(['backlight']).then(value =>{
this.log('+++++++++ lcd backlight report: ', value)
if (value.hasOwnProperty('backlight')) {
this.setSettings({ backlight: value['backlight'] })
}
})
//fault
if (this.hasCapability('t7e_zg_fault')){
this._thermostatCluster().readAttributes(["fault"]).then(value =>{
this.log('++++++++++ fault report ', value)
if (value.hasOwnProperty('fault')){
this.setCapabilityValue('t7e_zg_fault', value)
}
})
}
}
}
module.exports = thermostat_t7e_zg_thermostat;
**CHANGE run mode,then call _start() method. **