Hi, i created a method for interfacing an ESP Altherma bridge with Homey .
You need an esp32 with a firmware can bridge data to json data
You can use it for reading value from your altherma bridge and populate a virtual device created with Device Capabilities app.
Actually it only read some info.
Prerequisites:
- You need a esp32 with my fork of ESPAltherma for reading data in JSON format. See github url for configure it
- Install “Device Capabilities ” app from the Homey App Store.
- Install HomeyScript app from the Homey App Store
Step 1: Create a homeyscript for get the ESP Altherma data (example esp_altherma_bridge.js)
const b_ip = 'YOUR_ESP_ALTHERMA_IP';
const b_baseurl = 'http://'+ b_ip + '/getData';
function toFixed(num) {
return Math.trunc((num * 100) / 100);
}
data = {};
try {
const res = await fetch(b_baseurl);
if (res.ok) data = await res.json();
} catch (err) {
log(err);
}
let ops = [];
altherma = {};
switch(data['Operation Mode']) {
case 'Fan Only':
altherma['mode'] = '-';
break;
case 'Heating':
altherma['mode'] = 'Heating';
break;
case 'Cooling':
altherma['mode'] = 'Cooling';
break;
default:
altherma['mode'] = data['Operation Mode'];
}
switch(data['I/U operation mode']) {
case 'DHW':
altherma['status'] = 'On';
altherma['mode'] = 'DHW';
break;
case 'Stop':
altherma['status'] = '-';
break;
default:
altherma['status'] = 'On';
}
//if (altherma['status'] == 'Stop') altherma['status'] = 'Off';
let voltage = Math.min(230, data['Voltage (N-phase) (V)']);
altherma['heat_pump_power'] = (data['INV primary current (A)'] * voltage);
altherma['heat_pump_power_2'] = (data['INV secondary current (A)'] * voltage);
altherma['valve_2way'] = (data['2way valve(On:Heat_Off:Cool)'] == 'ON' ? 'Heat' : 'Cool') + ' ('+ data['2way valve(On:Heat_Off:Cool)'] +')';
altherma['valve_3way'] = (data['3way valve(On:DHW_Off:Space)'] == 'OFF' ? 'Ambient' : 'DHW') + ' ('+ data['3way valve(On:DHW_Off:Space)'] +')';
altherma['thermostat'] = data['Thermostat ON/OFF'];
if (data['Defrost Operation'] == 'ON') ops.push('defrost');
if (data['Oil Return Operation'] == 'ON') ops.push('oil_return');
if (data['Brine Flow Switch'] == 'ON') ops.push('brine');
if (ops.length > 0) altherma['operations'] = ops.join(', ');
altherma['t_outdoor'] = data['R1T-Outdoor air temp.'];
altherma['t_acs'] = data['2nd Domestic hot water temperature'];
altherma['t_mandata'] = data['[HPSU] Tv inflow Temp (R1T)'];
altherma['t_mandataBuh'] = data['[HPSU] Tvbh inflow Temp after Buffer/BUH (R2T)'];
altherma['t_ritorno'] = data['[HPSU] Tr return Temp (R4T)'];
altherma['t_serbatoio_dhw'] = data['DHW tank temp. (R5T)'];
altherma['t_setpoint'] = data['LW setpoint (main)'];
altherma['dhw_setpoint'] = data['DHW setpoint'];
altherma['t_heatingTarget'] = data['Boiler Heating Target Temp.'];
altherma['flowSensor'] = (data['Flow sensor (l/min)'] > 0 ? (data['Flow sensor (l/min)'] * 60).toFixed(0) : 0);
altherma['waterPump'] = data['Water pump operation'];
altherma['heat_delivered'] = (data['Flow sensor (l/min)'] * 0.06 * 1.16 * (altherma['t_mandataBuh'] - altherma['t_ritorno']));
altherma['heat_delivered_alt'] = (data['Flow sensor (l/min)'] * 0.06 * 1.16 * (altherma['t_mandata'] - altherma['t_ritorno']));
altherma['backupHeater'] = 'OFF';
if (data['BUH Step1'] == 'ON' || data['BUH Step2'] == 'ON') altherma['backupHeater'] = 'ON';
altherma['dt_h'] = data['Target delta T heating'];
altherma['dt_c'] = data['Target delta T cooling'];
altherma['cop'] = 0;
altherma['cop_alt'] = 0;
if (data['Operation Mode'] == 'Heating' && data['Freeze Protection'] == 'OFF') {
let c = (altherma['heat_delivered'] / (altherma['heat_pump_power'] / 1000));
if (c > 0) altherma['cop'] = parseFloat(c.toFixed(1));
let c_alt = (altherma['heat_delivered_alt'] / (altherma['heat_pump_power'] / 1000));
if (c_alt > 0) altherma['cop_alt'] = parseFloat(c_alt.toFixed(1));
}
altherma['heat_pump_power'] = parseFloat(altherma['heat_pump_power'].toFixed(1));
altherma['heat_pump_power_2'] = parseFloat(altherma['heat_pump_power_2'].toFixed(1));
if (altherma['mode'] == 'Cooling') {
altherma['heat_delivered'] = 0;
altherma['heat_delivered_alt'] = 0;
}
altherma['heat_delivered'] = parseFloat(altherma['heat_delivered'].toFixed(1));
altherma['heat_delivered_alt'] = parseFloat(altherma['heat_delivered_alt'].toFixed(1));
let operation = '-';
if (altherma['defrost'] == 'ON') operation = 'defrost';
if (altherma['oil_return'] == 'ON') operation = 'oil_return';
if (altherma['brine'] == 'ON') operation = 'brine';
//await tag('esp_altherma_operation', operation);
await tag('esp_altherma_mode', altherma['mode']);
await tag('esp_altherma_status', altherma['status']);
await tag('esp_altherma_cop', altherma['cop']);
await tag('esp_altherma_acs', altherma['t_acs']);
await tag('esp_altherma_consum', altherma['heat_pump_power']);
await tag('esp_altherma_mandata', altherma['t_mandata']);
await tag('esp_altherma_ritorno', altherma['t_ritorno']);
altherma['heat_pump_power'] += ' Wh';
altherma['heat_pump_power_2'] += ' Wh';
altherma['heat_delivered'] += ' Wh';
altherma['heat_delivered_alt'] += ' Wh';
altherma['t_outdoor'] += '°';
altherma['t_acs'] += '°';
altherma['t_mandata'] += '°';
altherma['t_mandataBuh'] += '°';
altherma['t_ritorno'] += '°';
altherma['t_serbatoio_dhw'] += '°';
altherma['t_setpoint'] += '°';
altherma['dhw_setpoint'] += '°';
altherma['t_heatingTarget'] += '°';
altherma['flowSensor'] += ' l/h';
await tag('esp_altherma', JSON.stringify(altherma));
//log(data);
//log(altherma);
return true;
Step 2: Create a Virtual Device for your Altherma device
- With app or my.homey.com create a new “Device capabilities” device.
Step 3: Configuring Device Settings
- Define the capabilities your virtual device will have, you could also import this sample [TEF] String
[tef:AVD:"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":/tef]
Step 4: Populate the virtual device with a flow
This is a implementation sample, you can suggest optimization or fix