esphome:
name: nilan300
esp32:
board: nodemcu-32s
framework:
type: esp-idf
# Enable logging
logger:
level: INFO
baud_rate: 0
# Enable Home Assistant API
api:
ota:
platform: esphome
password: "9939debfff235f69eb37720d836f209c"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
uart:
rx_pin: GPIO16
tx_pin: GPIO17
parity: EVEN
baud_rate: 19200
id: uart_modbus
stop_bits: 1
modbus:
id: modbus_id
uart_id: uart_modbus
modbus_controller:
id: nilan_modbus_controller
address: 30
modbus_id: modbus_id
update_interval: 30s
external_components:
- source: github://Jopand/esphome_components@main
refresh: 0s
nilan:
select:
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Set operation mode"
id: nilan_set_operation_mode
address: 1002
force_new_range: true
use_write_multiple: true
value_type: U_WORD
optionsmap:
"Off" : 0
"Heat" : 1
"Cool" : 2
"Auto" : 3
binary_sensor:
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Summer mode"
id: nilan_is_summer
register_type: read
address: 1200
bitmask: 1
#- platform: modbus_controller
# modbus_controller_id: nilan_modbus_controller
# name: "Air filter alarm"
# id: nilan_filter_alarm
# register_type: read
# address: 101
# bitmask: 1
- platform: template
name: "Air filter alarm"
id: nilan_filter_alarm
lambda: !lambda |-
return (id(nilan_alarm_list_1_id).state == 19
|| id(nilan_alarm_list_2_id).state == 19
|| id(nilan_alarm_list_3_id).state == 19);
- platform: template
id: nilan_bypass_state
name: Bypass state
device_class: opening
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
id: nilan_bypass_open_request
register_type: holding
address: 102
on_state:
- lambda: !lambda |-
id(nilan_bypass_state).publish_state(true);
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
id: nilan_bypass_close_request
register_type: holding
address: 103
on_state:
- lambda: !lambda |-
id(nilan_bypass_state).publish_state(false);
sensor:
- platform: uptime
name: "Nilan 300 Uptime"
update_interval: 60s
- platform: wifi_signal
name: "Nilan 300 WiFi Signal"
update_interval: 60s
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Bus version"
id: nilan_bus_version
skip_updates: 60
internal: true
accuracy_decimals: 0
entity_category: diagnostic
register_type: read
address: 0
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Inlet (before heater) temperature"
id: nilan_inlet_temp_t2
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
value_type: S_WORD
register_type: read
address: 202
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Exhaust temperature"
id: nilan_exhaust_temp_t3
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
value_type: S_WORD
register_type: read
address: 203
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Outlet temperature"
id: nilan_outlet_temp_t4
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
value_type: S_WORD
register_type: read
address: 204
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Inlet (after heater) temperature"
id: nilan_inlet_temp_t7
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: read
address: 207
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Outdoor temperature"
id: nilan_outdoor_temp_t8
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
value_type: S_WORD
register_type: read
address: 208
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Humidity"
id: nilan_humidity
unit_of_measurement: '%'
accuracy_decimals: 1
device_class: humidity
register_type: read
address: 221
filters:
- multiply: 0.01
on_value:
- lambda: !lambda |-
id(aqi_humidity).publish_state( (x < 35) ? "Too Dry" :
(x < 65) ? "Perfect" :
"Too Humid");
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Exhaust fan speed"
id: nilan_exhaust_fan_speed
unit_of_measurement: '%'
icon: "mdi:percent"
accuracy_decimals: 0
register_type: holding
address: 200
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Inlet fan speed"
id: nilan_inlet_fan_speed
unit_of_measurement: '%'
icon: "mdi:percent"
accuracy_decimals: 0
register_type: holding
address: 201
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Controller temperature"
id: nilan_controller_temp_t0
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: read
address: 200
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Room temperature"
id: nilan_room_temp_t15
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
value_type: S_WORD
register_type: read
address: 215
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Active alarms"
id: nilan_active_alarms
accuracy_decimals: 0
register_type: read
address: 400
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Alarm list 1 id"
id: nilan_alarm_list_1_id
accuracy_decimals: 0
register_type: read
address: 401
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_1_date
accuracy_decimals: 0
register_type: read
address: 402
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_1_time
accuracy_decimals: 0
register_type: read
address: 403
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Alarm list 2 id"
id: nilan_alarm_list_2_id
accuracy_decimals: 0
register_type: read
address: 404
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_2_date
accuracy_decimals: 0
register_type: read
address: 405
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_2_time
accuracy_decimals: 0
register_type: read
address: 406
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Alarm list 3 id"
id: nilan_alarm_list_3_id
accuracy_decimals: 0
register_type: read
address: 407
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_3_date
accuracy_decimals: 0
register_type: read
address: 408
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
internal: true
id: nilan_alarm_list_3_time
accuracy_decimals: 0
register_type: read
address: 409
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Actual on/off state"
id: nilan_actual_on_off_state
accuracy_decimals: 0
register_type: read
address: 1000
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "AirFlow.VentSet"
id: nilan_airflow_vent_set
accuracy_decimals: 0
register_type: read
address: 1100
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "AirFlow.InletAct"
id: nilan_airflow_inlet_act
accuracy_decimals: 0
register_type: read
address: 1101
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "AirFlow.ExhaustAct"
id: nilan_airflow_exhaust_act
accuracy_decimals: 0
register_type: read
address: 1102
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Days since last air filter change alarm"
id: nilan_days_since_last_air_filter_alarm
accuracy_decimals: 0
register_type: read
address: 1103
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Days to next air filter change alarm"
id: nilan_days_to_next_air_filter_alarm
accuracy_decimals: 0
register_type: read
address: 1104
- platform: template
name: "Calculated difference exhaust and inlet"
id: calculated_value_inlet_exhaust
icon: "mdi:thermometer"
unit_of_measurement: °C
update_interval: 10s
lambda: 'return id(nilan_exhaust_temp_t3).state - id(nilan_inlet_temp_t7).state;'
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Heat exchange efficiency"
id: nilan_heat_exchange_efficiency
unit_of_measurement: '%'
accuracy_decimals: 1
register_type: read
address: 1204
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Cooling set temperature"
id: nilan_cooling_set_temp
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: holding
address: 1200
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Min summer temperature"
id: nilan_min_summer_temp
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: holding
address: 1201
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Min winter temperature"
id: nilan_min_winter_temp
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: holding
address: 1202
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Max summer temperature"
id: nilan_max_summer_temp
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: holding
address: 1203
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Max winter temperature"
id: nilan_max_winter_temp
unit_of_measurement: °C
accuracy_decimals: 1
device_class: temperature
register_type: holding
address: 1204
filters:
- multiply: 0.01
number:
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Ventilation speed set"
id: nilan_ventilation_speed_set
min_value: 0
max_value: 4
step: 1.0
address: 1003
force_new_range: true
use_write_multiple: true
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Temperature set"
id: nilan_target_temperature_set
min_value: 12
max_value: 30
step: 1.0
address: 1004
force_new_range: true
use_write_multiple: true
multiply: 100
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Humidity low vent step"
id: humidity_low_vent_step
min_value: 0.0
max_value: 4.0
step: 1.0
address: 1910
force_new_range: true
use_write_multiple: true
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Humidity high vent step"
id: humidity_high_vent_step
min_value: 0.0
max_value: 4.0
step: 1.0
address: 1911
force_new_range: true
use_write_multiple: true
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Humidity limit low vent"
id: humidity_limit_low_vent
unit_of_measurement: '%'
icon: "mdi:percent"
min_value: 15.0
max_value: 45.0
step: 1.0
address: 1912
force_new_range: true
use_write_multiple: true
multiply: 100
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Humidity max time on high ventilation (minutes)"
id: humidity_max_time_high_vent
value_type: U_WORD
unit_of_measurement: minutes
min_value: 0.0
max_value: 180.0
step: 1.0
address: 1913
force_new_range: true
use_write_multiple: true
icon: mdi:clock-start
text_sensor:
- platform: template
name: "Air Quality Index Humidity (AQI)"
id: aqi_humidity
icon: mdi:water-percent
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Operation mode"
id: nilan_operation_mode
register_type: read
address: 1001
lambda: !lambda |-
uint16_t int_mode = (data[item->offset] << 8) + data[item->offset+1];
ESP_LOGD("main","Parsed operation mode int : %d", int_mode);
std::string mode_str;
switch (int_mode) {
case 0: mode_str = "Off"; break;
case 1: mode_str = "Heat"; break;
case 2: mode_str = "Cool"; break;
case 3: mode_str = "Auto"; break;
case 4: mode_str = "Service"; break;
default: mode_str = "Unknown"; break;
}
return mode_str;
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Control mode"
id: nilan_control_state
register_type: read
address: 1002
lambda: !lambda |-
uint16_t int_mode = (data[item->offset] << 8) + data[item->offset+1];
ESP_LOGD("main","Parsed operation mode int : %d", int_mode);
std::string mode_str;
switch (int_mode) {
case 0: mode_str = "Off"; break;
case 1: mode_str = "Shift"; break;
case 2: mode_str = "Stop"; break;
case 3: mode_str = "Start"; break;
case 4: mode_str = "Standby"; break;
case 5: mode_str = "Ventilation stop"; break;
case 6: mode_str = "Ventilation"; break;
case 7: mode_str = "Heating"; break;
case 8: mode_str = "Cooling"; break;
case 9: mode_str = "Hot water"; break;
case 10: mode_str = "Legionella"; break;
case 11: mode_str = "Cooling + hot water"; break;
case 12: mode_str = "Central heating"; break;
case 13: mode_str = "Defrost"; break;
case 14: mode_str = "Frost secure"; break;
case 15: mode_str = "Service"; break;
case 16: mode_str = "Alarm"; break;
default: mode_str = "Unknown"; break;
}
return mode_str;
- platform: template
name: "Alarm 1 timestamp"
id: nilan_alarm_list_1_datetime
lambda: |-
uint16_t alarmDate = id(nilan_alarm_list_1_date).state;
uint16_t alarmTime = id(nilan_alarm_list_1_time).state;
if(!alarmDate ||!alarmTime) return std::string("");
// Parse the date
auto year = (alarmDate >> 9) + 1980;
auto month = (alarmDate >> 5) & 0b00001111;
auto day = alarmDate & 0b00011111;
auto hour = (alarmTime >> 11) & 0b00011111;
auto minute = (alarmTime >> 5) & 0b0011111;
auto second = (alarmTime & 0b00011111) * 2;
return std::to_string(year) + "-" +
((month < 10) ? "0" : "") + std::to_string(month) + "-" +
((day < 10) ? "0" : "") + std::to_string(day) + " " +
((hour < 10) ? "0" : "") + std::to_string(hour) + ":" +
((minute < 10) ? "0" : "") + std::to_string(minute) + ":" +
((second < 10) ? "0" : "") + std::to_string(second);
- platform: template
name: "Alarm 2 timestamp"
id: nilan_alarm_list_2_datetime
lambda: |-
uint16_t alarmDate = id(nilan_alarm_list_2_date).state;
uint16_t alarmTime = id(nilan_alarm_list_2_time).state;
if(!alarmDate ||!alarmTime) return std::string("");
// Parse the date
auto year = (alarmDate >> 9) + 1980;
auto month = (alarmDate >> 5) & 0b00001111;
auto day = alarmDate & 0b00011111;
auto hour = (alarmTime >> 11) & 0b00011111;
auto minute = (alarmTime >> 5) & 0b0011111;
auto second = (alarmTime & 0b00011111) * 2;
return std::to_string(year) + "-" +
((month < 10) ? "0" : "") + std::to_string(month) + "-" +
((day < 10) ? "0" : "") + std::to_string(day) + " " +
((hour < 10) ? "0" : "") + std::to_string(hour) + ":" +
((minute < 10) ? "0" : "") + std::to_string(minute) + ":" +
((second < 10) ? "0" : "") + std::to_string(second);
- platform: template
name: "Alarm 3 timestamp"
id: nilan_alarm_list_3_datetime
lambda: |-
uint16_t alarmDate = id(nilan_alarm_list_3_date).state;
uint16_t alarmTime = id(nilan_alarm_list_3_time).state;
if(!alarmDate ||!alarmTime) return std::string("");
// Parse the date
auto year = (alarmDate >> 9) + 1980;
auto month = (alarmDate >> 5) & 0b00001111;
auto day = alarmDate & 0b00011111;
auto hour = (alarmTime >> 11) & 0b00011111;
auto minute = (alarmTime >> 5) & 0b0011111;
auto second = (alarmTime & 0b00011111) * 2;
return std::to_string(year) + "-" +
((month < 10) ? "0" : "") + std::to_string(month) + "-" +
((day < 10) ? "0" : "") + std::to_string(day) + " " +
((hour < 10) ? "0" : "") + std::to_string(hour) + ":" +
((minute < 10) ? "0" : "") + std::to_string(minute) + ":" +
((second < 10) ? "0" : "") + std::to_string(second);
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Software version"
id: nilan_version_info
skip_updates: 60
register_type: read
register_count: 3
response_size: 6
address: 1
entity_category: diagnostic
lambda: !lambda |-
float bus_version = nilan_bus_version->get_state();
std::string output;
if (bus_version == 8) {
output = data[item->offset+1];
output += data[item->offset+0];
output += data[item->offset+3];
output += data[item->offset+2];
output += data[item->offset+5];
output += data[item->offset+4];
} else {
output = data[item->offset+0];
output += data[item->offset+1];
output += '.';
output += data[item->offset+2];
output += data[item->offset+3];
output += '.';
output += data[item->offset+4];
output += data[item->offset+5];
}
ESP_LOGD("main","Parsed SW version: %s", output.c_str());
return output;
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "Aggregate type"
id: nilan_platform_id
skip_updates: 60
entity_category: diagnostic
register_type: holding
address: 1000
lambda: !lambda |-
uint16_t int_type = (data[item->offset] << 8) + data[item->offset+1];
ESP_LOGD("main","Parsed aggregate type int : %d", int_type);
std::string type_str;
switch (int_type) {
case 0: type_str = "None"; break;
case 1: type_str = "Test"; break;
case 2: type_str = "VPL 10 uden køl"; break;
case 3: type_str = "VPL 15 uden køl"; break;
case 4: type_str = "VPL 15 med køl"; break;
case 5: type_str = "VPL 25 med 3 hastigheder uden køl"; break;
case 6: type_str = "VPL 25 med 3 hastigheder med køl"; break;
case 7: type_str = "VPL 28 2 hastigheder uden køl"; break;
case 8: type_str = "VPL 28 med 2 hastigheder med køl"; break;
case 9: type_str = "VP 18 med kryds monteret oven på anlæg uden køl"; break;
case 10: type_str = "VP 18 med kryds monteret oven på anlæg med køl"; break;
case 11: type_str = "Vp 18 Compact og Compact p uden køl"; break;
case 12: type_str = "VP 18 Compact og Compact P med køl"; break;
case 13: type_str = "Comfort anlæg (Comfort 300 LR)"; break;
case 14: type_str = "CT 150 anlæg med 1-2-3 omskifter"; break;
case 15: type_str = "VLX som kører VAV"; break;
case 16: type_str = "VLX med 2 trin"; break;
case 17: type_str = "VLX med 3 trin"; break;
case 18: type_str = "VP 18 uden køl"; break;
case 19: type_str = "VP 18 med køl"; break;
case 20: type_str = "VP 18 med elkedel uden køl"; break;
case 21: type_str = "VP 18 med elkedel og køl"; break;
case 22: type_str = "VGU 250 brugsvands varmepumpe"; break;
case 23: type_str = "VGU 250 brugsvands varmepumpe med elkedel"; break;
case 24: type_str = "VPL 25 uden køl"; break;
case 25: type_str = "VPL 25 med køl"; break;
case 26: type_str = "VPM 120-560"; break;
case 27: type_str = "Comfort 1200 - 4000"; break;
case 28: type_str = "VP 20 Compact gorona"; break;
case 29: type_str = "VLX med CTS 602 print"; break;
case 30: type_str = "Compact P Nordic"; break;
case 31: type_str = "Comfort Nordic"; break;
case 32: type_str = "VP 18 Version 1"; break;
case 33: type_str = "Combi 300"; break;
case 34: type_str = "Compact med 4-vejsventil uden køl"; break;
default: type_str = "Unknown"; break;
}
return type_str;
switch:
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
name: "On/Off state"
id: nilan_on_off_state
register_type: holding
bitmask: 1
force_new_range: true
use_write_multiple: true
address: 1001
climate:
- platform: nilan
name: Nilan
current_temp_sensor_id: nilan_inlet_temp_t7
target_temp_sensor_id: nilan_target_temperature_set
fan_speed_sensor_id: nilan_ventilation_speed_set
mode_select_id: nilan_set_operation_mode
output:
- platform: modbus_controller
modbus_controller_id: nilan_modbus_controller
id: nilan_reset_alarm
address: 400
use_write_multiple: true
value_type: U_WORD
write_lambda: return 255;
button:
- platform: output
name: Reset alarm
id: nilan_reset_alarm_button
output: nilan_reset_alarm
duration: 5s