Skip to content

Commit

Permalink
Add support to optional CO2 sensor (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
kotope authored Sep 20, 2023
1 parent 6ce48a8 commit 98098a4
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ https://github.com/dirtyha/my-esp8266/tree/master/Vallox
- Vallox 121 SE (version without front heating module)
- Vallox 121 SE (version with front heating)
- Vallox 150 SE
- Vallox 270 SE
- Vallox Digit SE
- Vallox Digit SE 2
- Vallox ValloPlus 350 SE
Expand All @@ -25,6 +26,7 @@ Contained entities:
* sensor: switch type to see if switch is configured as fireplace or boost
* sensors: outside, inside, exhaust and incoming temperatures
* sensors: rh1 and rh2 if supported by your Vallox Ventilation machine
* sensor: co2 if supported by your Vallox Ventilation machine
* binary sensors: motor in/motor out statuses
* binary sensor: summer mode
* sensor: service counter (how many months until you have to replace your filters)
Expand Down
1 change: 1 addition & 0 deletions custom_components/vallox2mqtt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ async def async_setup_entry(hass, entry, async_add_devices):
ValloxDigitAttributedSensor(hass, entry, "RH 2", 'rh_2', "%", "mdi:water-percent"),
ValloxDigitAttributedSensor(hass, entry, "Service Counter", 'service_counter', "Months"),
ValloxDigitAttributedSensor(hass, entry, "Switch Type", 'switch_type', ""),
ValloxDigitAttributedSensor(hass, entry, "CO2", 'co2', "ppm", "mdi:molecule-co2"),
])

class ValloxDigitSensor(Entity):
Expand Down
2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"name": "vallox2mqtt",
"country": "EN",
"domains": ["sensor", "climate", "binary_sensor"],
"homeassistant": "2021.12.0",
"homeassistant": "2023.6.0",
"iot_class": ["Local Push"]
}
49 changes: 39 additions & 10 deletions valloxesp/Vallox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ int Vallox::getRh2() {
return data.rh2.value;
}

int Vallox::getCO2() {
if (!data.co2.lastReceived) {
return NOT_SET;
}
return data.co2.value;
}

int Vallox::getHeatingTarget() {
return data.heating_target.value;
}
Expand Down Expand Up @@ -530,20 +537,36 @@ void Vallox::decodeMessage(const byte message[]) {

// Temperature (status object)
if (variable == VX_VARIABLE_T_OUTSIDE) { // OUTSIDE
checkTemperatureChange(&(data.t_outside.value), ntc2Cel(value), &(data.t_outside.lastReceived));
checkValueChange(&(data.t_outside.value), ntc2Cel(value), &(data.t_outside.lastReceived));
} else if (variable == VX_VARIABLE_T_EXHAUST) { // EXHAUST
checkTemperatureChange(&(data.t_exhaust.value), ntc2Cel(value), &(data.t_exhaust.lastReceived));
checkValueChange(&(data.t_exhaust.value), ntc2Cel(value), &(data.t_exhaust.lastReceived));
} else if (variable == VX_VARIABLE_T_INSIDE) { // INSIDE
checkTemperatureChange(&(data.t_inside.value), ntc2Cel(value), &(data.t_inside.lastReceived));
checkValueChange(&(data.t_inside.value), ntc2Cel(value), &(data.t_inside.lastReceived));
} else if (variable == VX_VARIABLE_T_INCOMING) { // INCOMING
checkTemperatureChange(&(data.t_incoming.value), ntc2Cel(value), &(data.t_incoming.lastReceived));
checkValueChange(&(data.t_incoming.value), ntc2Cel(value), &(data.t_incoming.lastReceived));
}

// RH
else if (variable == VX_VARIABLE_RH1) {
checkTemperatureChange(&(data.rh1.value), hex2Rh(value), &(data.rh1.lastReceived));
checkValueChange(&(data.rh1.value), hex2Rh(value), &(data.rh1.lastReceived));
} else if (variable == VX_VARIABLE_RH2) {
checkTemperatureChange(&(data.rh2.value), hex2Rh(value), &(data.rh2.lastReceived));
checkValueChange(&(data.rh2.value), hex2Rh(value), &(data.rh2.lastReceived));
}

// CO2
// Let's assume that the timeinterval for the same value is something pre-defined..
else if (variable == VX_VARIABLE_CO2_HI) {
data.co2_hi.lastReceived = millis();
data.co2_hi.value = value;
if (data.co2_lo.lastReceived > millis() - CO2_LIFE_TIME_MS) {
handleCo2TotalValue(data.co2_hi.value, data.co2_lo.value);
}
} else if (variable == VX_VARIABLE_CO2_LO) {
data.co2_lo.lastReceived = millis();
data.co2_lo.value = value;
if (data.co2_hi.lastReceived > millis() - CO2_LIFE_TIME_MS) {
handleCo2TotalValue(data.co2_hi.value, data.co2_lo.value);
}
}

// Others (config object)
Expand Down Expand Up @@ -629,7 +652,6 @@ void Vallox::decodeProgram(byte program) {

checkSettingsChange(&(settings.is_boost_setting.value), (program & VX_PROGRAM_SWITCH_TYPE) != 0x00);

// TODO:
if (shoudInformCallback) {
// Never received, publish
statusChangedCallback();
Expand Down Expand Up @@ -685,14 +707,15 @@ void Vallox::checkStatusChange(int* oldValue, int newValue) {

//
// Temperature change
void Vallox::checkTemperatureChange(int *oldValue, int newValue, unsigned long *lastReceived) {
void Vallox::checkValueChange(int *oldValue, int newValue, unsigned long *lastReceived) {
unsigned long now = millis();

*lastReceived = now;
checkTemperatureChange(oldValue, newValue);
checkValueChange(oldValue, newValue);
}

void Vallox::checkTemperatureChange(int* oldValue, int newValue) {
// Check for value change (Temperature, CO2, RH)
void Vallox::checkValueChange(int* oldValue, int newValue) {
if (checkChange(oldValue, newValue) && isTemperatureInitDone()) { // Do not publish status, until base values has been received
temperatureChangedCallback();
}
Expand Down Expand Up @@ -849,3 +872,9 @@ boolean Vallox::isStatusInitDone() { // all initializations
data.service_counter.lastReceived &&
data.heating_target.lastReceived;
}

void Vallox::handleCo2TotalValue(byte hi, byte low) {
// Construct co2 value from hi and lo bytes
uint16_t total = low + (hi << 8);
checkValueChange(&(data.co2.value), total, &(data.co2.lastReceived));
}
22 changes: 20 additions & 2 deletions valloxesp/Vallox.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#define VX_MSG_THIS_PANEL 0x22 // This panel address, should not be same with other panel(s)
#define VX_MSG_PANELS 0x20

#define CO2_LIFE_TIME_MS 2000 // Maximum time that LO and HI bytes are considered to be the same value

#define DEBUG_PRINT_CALLBACK_SIGNATURE std::function<void(String debugPrint)> debugPrintCallback
#define PACKET_CALLBACK_SIGNATURE std::function<void(byte* packet, unsigned int length, char* packetDirection)> packetCallback
#define STATUS_CHANGED_CALLBACK_SIGNATURE std::function<void()> statusChangedCallback
Expand All @@ -35,6 +37,11 @@ struct booleanValue {
boolean value;
unsigned long lastReceived;
};

struct byteValue {
byte value;
unsigned long lastReceived;
};

class Vallox {
public:
Expand Down Expand Up @@ -81,6 +88,7 @@ class Vallox {
int getDefaultFanSpeed();
int getRh1();
int getRh2();
int getCO2();
int getServicePeriod();
int getServiceCounter();
int getHeatingTarget();
Expand Down Expand Up @@ -150,6 +158,12 @@ class Vallox {
intValue rh1;
intValue rh2;

// CO2
byteValue co2_hi;
byteValue co2_lo;

intValue co2; // Combined from hi + lo bytes

// 08 variables
booleanValue is_summer_mode;
booleanValue is_error;
Expand Down Expand Up @@ -242,13 +256,17 @@ class Vallox {
bool isStatusInitDone(); // Checks that all init poll requests has been done
bool isTemperatureInitDone(); // Checks that all temperature values has been received at once

// Checks if status has been changed (heating mode, etc..)
void checkStatusChange(boolean* oldValue, boolean newValue);
void checkStatusChange(int* oldValue, int newValue);

void checkTemperatureChange(int* oldValue, int newValue);
void checkTemperatureChange(int *oldValue, int newValue, unsigned long* lastReceived);
// Checks if a push value has been changed (temp, CO2, RH)
void checkValueChange(int* oldValue, int newValue);
void checkValueChange(int *oldValue, int newValue, unsigned long* lastReceived);

void checkSettingsChange(boolean* oldValue, boolean newValue);

void handleCo2TotalValue(byte hi, byte low);
};

#endif
6 changes: 6 additions & 0 deletions valloxesp/vallox_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
#define VX_VARIABLE_HEATING_STATUS 0x07 // TODO: Not yet implemented
#define VX_VARIABLE_PROGRAM 0xAA

// Order of these two seems to be that the panel first queries for LO and then HI
// Query interval is something like 5s by the panel..
#define VX_VARIABLE_CO2_HI 0x2B
#define VX_VARIABLE_CO2_LO 0x2C


// status flags of variable A3
#define VX_STATUS_FLAG_POWER 0x01 // bit 0 read/write
#define VX_STATUS_FLAG_CO2 0x02 // bit 1 read/write
Expand Down
6 changes: 5 additions & 1 deletion valloxesp/valloxesp.ino
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#define JSON_BUFFER_LENGTH 2048
#define DEBUG false // default value for debug

#define VALLOXESP_VERSION "0.9.0" // this version
#define VALLOXESP_VERSION "1.0.0" // this version

// Callbacks
void mqttCallback(char* topic, byte* payload, unsigned int payloadLength);
Expand Down Expand Up @@ -235,6 +235,10 @@ void publishTemperatures() {
root["rh_2"] = vx.getRh2();
}

if(vx.getCO2() != NOT_SET) {
root["co2"] = vx.getCO2();
}

String mqttOutput;
serializeJson(root, mqttOutput);
client.beginPublish(vallox_temp_topic, mqttOutput.length(), true);
Expand Down

0 comments on commit 98098a4

Please sign in to comment.