Skip to content

Commit

Permalink
feat: Floor sensor support for Danfoss Icon (#7566)
Browse files Browse the repository at this point in the history
* Danfoss Icon converter clean up

* Use default reporting values for battery percentage, thermostat temperature, thermostat setpoint and output status

* Add floor sensor support for Danfoss Icon
  • Loading branch information
tomash345 authored May 27, 2024
1 parent ee40ebe commit 5aa7d27
Showing 1 changed file with 110 additions and 54 deletions.
164 changes: 110 additions & 54 deletions src/devices/danfoss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,27 +229,32 @@ const definitions: Definition[] = [
],
model: 'Icon',
vendor: 'Danfoss',
description: 'Icon floor heating (regulator, Zigbee module & thermostats)',
description: 'Icon Main Controller with Zigbee Module, Room Thermostat',
fromZigbee: [
fz.danfoss_icon_regulator,
fz.danfoss_thermostat,
fz.danfoss_icon_battery,
fz.thermostat],
fz.thermostat,
fz.danfoss_thermostat,
fz.danfoss_icon_regulator,
fz.danfoss_icon_floor_sensor,
fz.temperature,
],
toZigbee: [
tz.thermostat_local_temperature,
tz.thermostat_occupied_heating_setpoint,
tz.thermostat_system_mode,
tz.thermostat_running_state,
tz.thermostat_min_heat_setpoint_limit,
tz.thermostat_max_heat_setpoint_limit,
tz.danfoss_output_status,
tz.thermostat_system_mode,
tz.danfoss_room_status_code,
tz.danfoss_system_status_water,
tz.danfoss_output_status,
tz.danfoss_floor_sensor_mode,
tz.danfoss_floor_min_setpoint,
tz.danfoss_floor_max_setpoint,
tz.temperature,
tz.danfoss_system_status_code,
tz.danfoss_system_status_water,
tz.danfoss_multimaster_role,
],
meta: {multiEndpoint: true, thermostat: {dontMapPIHeatingDemand: true}},
// ota: ota.zigbeeOTA,
endpoint: (device) => {
return {
'l1': 1, 'l2': 2, 'l3': 3, 'l4': 4, 'l5': 5,
Expand All @@ -259,40 +264,84 @@ const definitions: Definition[] = [
},
exposes: [].concat(((endpointsCount) => {
const features = [];

for (let i = 1; i <= endpointsCount; i++) {
const epName = `l${i}`;
if (i!=16) {

if (i < 16) {
features.push(e.battery().withEndpoint(epName));

features.push(e.climate().withSetpoint('occupied_heating_setpoint', 5, 35, 0.5)
.withLocalTemperature().withRunningState(['idle', 'heat']).withSystemMode(['heat']).withEndpoint(epName));
.withLocalTemperature()
.withSystemMode(['heat'])
.withRunningState(['idle', 'heat'], ea.STATE)
.withEndpoint(epName));

features.push(e.numeric('abs_min_heat_setpoint_limit', ea.STATE)
.withUnit('°C').withEndpoint(epName)
.withUnit('°C')
.withEndpoint(epName)
.withDescription('Absolute min temperature allowed on the device'));
features.push(e.numeric('abs_max_heat_setpoint_limit', ea.STATE)
.withUnit('°C').withEndpoint(epName)
.withUnit('°C')
.withEndpoint(epName)
.withDescription('Absolute max temperature allowed on the device'));

features.push(e.numeric('min_heat_setpoint_limit', ea.ALL)
.withValueMin(4).withValueMax(35).withValueStep(0.5).withUnit('°C')
.withEndpoint(epName).withDescription('Min temperature limit set on the device'));
.withValueMin(4)
.withValueMax(35)
.withValueStep(0.5)
.withUnit('°C')
.withEndpoint(epName)
.withDescription('Min temperature limit set on the device'));
features.push(e.numeric('max_heat_setpoint_limit', ea.ALL)
.withValueMin(4).withValueMax(35).withValueStep(0.5).withUnit('°C')
.withEndpoint(epName).withDescription('Max temperature limit set on the device'));
.withValueMin(4)
.withValueMax(35)
.withValueStep(0.5)
.withUnit('°C')
.withEndpoint(epName)
.withDescription('Max temperature limit set on the device'));

features.push(e.enum('setpoint_change_source', ea.STATE, ['manual', 'schedule', 'externally'])
.withEndpoint(epName));

features.push(e.enum('output_status', ea.STATE_GET, ['inactive', 'active'])
.withEndpoint(epName).withDescription('Danfoss Output Status [Active vs Inactive])'));
features.push(e.enum('room_status_code', ea.STATE_GET, ['no_error', 'missing_rt',
'rt_touch_error', 'floor_sensor_short_circuit', 'floor_sensor_disconnected'])
.withEndpoint(epName).withDescription('Thermostat status'));
.withEndpoint(epName)
.withDescription('Actuator status'));

features.push(e.enum('room_status_code', ea.STATE_GET, ['no_error', 'missing_rt', 'rt_touch_error',
'floor_sensor_short_circuit', 'floor_sensor_disconnected'])
.withEndpoint(epName)
.withDescription('Thermostat status'));

features.push(e.enum('room_floor_sensor_mode', ea.STATE_GET, ['comfort', 'floor_only', 'dual_mode'])
.withEndpoint(epName)
.withDescription('Floor sensor mode'));
features.push(e.numeric('floor_min_setpoint', ea.ALL)
.withValueMin(18).withValueMax(35).withValueStep(0.5).withUnit('°C')
.withEndpoint(epName)
.withDescription('Min floor temperature'));
features.push(e.numeric('floor_max_setpoint', ea.ALL)
.withValueMin(18).withValueMax(35).withValueStep(0.5).withUnit('°C')
.withEndpoint(epName)
.withDescription('Max floor temperature'));

features.push(e.numeric('temperature', ea.STATE_GET)
.withUnit('°C')
.withEndpoint(epName)
.withDescription('Floor temperature'));
} else {
features.push(e.enum('system_status_code', ea.STATE_GET, ['no_error', 'missing_expansion_board',
'missing_radio_module', 'missing_command_module', 'missing_master_rail', 'missing_slave_rail_no_1',
'missing_slave_rail_no_2', 'pt1000_input_short_circuit', 'pt1000_input_open_circuit',
'error_on_one_or_more_output']).withEndpoint('l16').withDescription('Regulator Status'));
'error_on_one_or_more_output'])
.withEndpoint('l16')
.withDescription('Main Controller Status'));
features.push(e.enum('system_status_water', ea.STATE_GET, ['hot_water_flow_in_pipes', 'cool_water_flow_in_pipes'])
.withEndpoint('l16').withDescription('Water Status of Regulator'));
.withEndpoint('l16')
.withDescription('Main Controller Water Status'));
features.push(e.enum('multimaster_role', ea.STATE_GET, ['invalid_unused', 'master', 'slave_1', 'slave_2'])
.withEndpoint('l16').withDescription('Regulator role (Master vs Slave)'));
.withEndpoint('l16')
.withDescription('Main Controller Role'));
}
}

Expand All @@ -303,50 +352,56 @@ const definitions: Definition[] = [

for (let i = 1; i <= 15; i++) {
const endpoint = device.getEndpoint(i);

if (typeof endpoint !== 'undefined') {
await reporting.bind(endpoint, coordinatorEndpoint,
['genPowerCfg', 'hvacThermostat', 'hvacUserInterfaceCfg']);
await reporting.batteryPercentageRemaining(endpoint,
{min: constants.repInterval.HOUR, max: 43200, change: 1});
await reporting.thermostatTemperature(endpoint,
{min: 0, max: constants.repInterval.MINUTES_10, change: 10});
await reporting.thermostatOccupiedHeatingSetpoint(endpoint,
{min: 0, max: constants.repInterval.MINUTES_10, change: 10});
await reporting.bind(endpoint, coordinatorEndpoint, [
'genPowerCfg',
'hvacThermostat',
]);

await reporting.batteryPercentageRemaining(endpoint);
await reporting.thermostatTemperature(endpoint);
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
await reporting.temperature(endpoint, {change: 10});

await endpoint.configureReporting('hvacThermostat', [{
attribute: 'danfossOutputStatus',
minimumReportInterval: constants.repInterval.MINUTE,
maximumReportInterval: constants.repInterval.MINUTES_10,
minimumReportInterval: 0,
maximumReportInterval: constants.repInterval.HOUR,
reportableChange: 1,
}], options);

// Danfoss Icon Thermostat Specific
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
await endpoint.read('hvacThermostat', [
'localTemp',
'occupiedHeatingSetpoint',
'setpointChangeSource',
'absMinHeatSetpointLimit',
'absMaxHeatSetpointLimit',
'minHeatSetpointLimit',
'maxHeatSetpointLimit',
'systemMode',
]);
await endpoint.read('hvacThermostat', [
'danfossRoomStatusCode',
'danfossOutputStatus',
'danfossRoomStatusCode'], options);

// Standard Thermostat
await endpoint.read('hvacThermostat', ['localTemp']);
await endpoint.read('hvacThermostat', ['occupiedHeatingSetpoint']);
await endpoint.read('hvacThermostat', ['systemMode']);
await endpoint.read('hvacThermostat', ['setpointChangeSource']);
await endpoint.read('hvacThermostat', ['absMinHeatSetpointLimit']);
await endpoint.read('hvacThermostat', ['absMaxHeatSetpointLimit']);
await endpoint.read('hvacThermostat', ['minHeatSetpointLimit']);
await endpoint.read('hvacThermostat', ['maxHeatSetpointLimit']);
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
'danfossRoomFloorSensorMode',
'danfossFloorMinSetpoint',
'danfossFloorMaxSetpoint',
], options);
}
}

// Danfoss Icon Regulator Specific
const endpoint232 = device.getEndpoint(232);
// Danfoss Icon Main Controller Specific Endpoint
const mainController = device.getEndpoint(232);

await reporting.bind(endpoint232, coordinatorEndpoint, ['haDiagnostic']);
await reporting.bind(mainController, coordinatorEndpoint, ['haDiagnostic']);

await endpoint232.read('haDiagnostic', [
await mainController.read('haDiagnostic', [
'danfossSystemStatusCode',
'danfossSystemStatusWater',
'danfossMultimasterRole'], options);
'danfossMultimasterRole',
], options);
},
},
{
Expand Down Expand Up @@ -419,7 +474,7 @@ const definitions: Definition[] = [

features.push(e.enum('output_status', ea.STATE_GET, ['inactive', 'active'])
.withEndpoint(epName)
.withDescription('Actuator status)'));
.withDescription('Actuator status'));

features.push(e.enum('room_status_code', ea.STATE_GET, ['no_error', 'missing_rt', 'rt_touch_error',
'floor_sensor_short_circuit', 'floor_sensor_disconnected'])
Expand Down Expand Up @@ -462,6 +517,7 @@ const definitions: Definition[] = [
.withDescription('Main Controller Role'));
}
}

return features;
})(16)),
configure: async (device, coordinatorEndpoint) => {
Expand Down Expand Up @@ -520,7 +576,7 @@ const definitions: Definition[] = [
}], options);

await endpoint.read('hvacThermostat', ['setpointChangeSource']);
await endpoint.read('hvacThermostat', ['danfossOutputStatus', 'danfossRoomStatusCode'], options);
await endpoint.read('hvacThermostat', ['danfossRoomStatusCode', 'danfossOutputStatus'], options);
}
}

Expand Down

0 comments on commit 5aa7d27

Please sign in to comment.