mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
Merge pull request #2647 from MichaelDvP/dev
fixes and additions, see description
This commit is contained in:
@@ -18,11 +18,15 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
||||
- disinfection command [#2601](https://github.com/emsesp/EMS-ESP32/issues/2601)
|
||||
- added new board profile for upcoming BBQKees E32V2.2
|
||||
- set differential pressure entity in Mixer device
|
||||
- set set climate action cooling/heating in HA
|
||||
- set set climate action cooling/heating in HA [#2583](https://github.com/emsesp/EMS-ESP32/issues/2583)
|
||||
- Internal sensors of E32V2_2
|
||||
- FW200 display options
|
||||
- CR11 mode settings OFF/MANUAL depends on selTemp
|
||||
- FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610)
|
||||
- CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437)
|
||||
- Fuse settings for BBQKees boards
|
||||
- Analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
|
||||
- Analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
|
||||
- SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636)
|
||||
- Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645)
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -39,9 +43,13 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
||||
- Add pulsed water meter input to V1.3 gateway with Lilygo S3 [#2550](https://github.com/emsesp/EMS-ESP32/issues/2550)
|
||||
- fix missing long 10-second press of Button to perform a factory reset
|
||||
- fix wwMaxPower on Junkers ZBS14 [#2609](https://github.com/emsesp/EMS-ESP32/issues/2609)
|
||||
- ventilation bypass state from telegram 0x55C [#1197](https://github.com/emsesp/EMS-ESP32/issues/1197)
|
||||
- set selflowtemp for ems+ boilers [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
|
||||
|
||||
## Changed
|
||||
|
||||
- show console log with ISO date/time [#2533](https://github.com/emsesp/EMS-ESP32/discussions/2533)
|
||||
- remove ESP32 CPU temperature
|
||||
- updated core libraries like AsyncTCP, AsyncWebServer and Modbus
|
||||
- remove command `scan deep`
|
||||
- ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
|
||||
|
||||
@@ -439,7 +439,8 @@ const Sensors = () => {
|
||||
<Cell>{a.n}</Cell>
|
||||
<Cell stiff>{AnalogTypeNames[a.t]} </Cell>
|
||||
{(a.t === AnalogType.DIGITAL_OUT && a.g !== 25 && a.g !== 26) ||
|
||||
a.t === AnalogType.DIGITAL_IN ? (
|
||||
a.t === AnalogType.DIGITAL_IN ||
|
||||
a.t === AnalogType.PULSE ? (
|
||||
<Cell stiff>{a.v ? LL.ON() : LL.OFF()}</Cell>
|
||||
) : (
|
||||
<Cell stiff>{a.t ? formatValue(a.v, a.u) : ''}</Cell>
|
||||
|
||||
@@ -132,7 +132,9 @@ const SensorsAnalogDialog = ({
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
{editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && (
|
||||
{((editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE) ||
|
||||
(editItem.t >= AnalogType.FREQ_0 &&
|
||||
editItem.t <= AnalogType.FREQ_2)) && (
|
||||
<Grid>
|
||||
<TextField
|
||||
name="u"
|
||||
@@ -348,6 +350,42 @@ const SensorsAnalogDialog = ({
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
{editItem.t === AnalogType.PULSE && (
|
||||
<>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="o"
|
||||
label={LL.POLARITY()}
|
||||
value={editItem.o}
|
||||
sx={{ width: '11ch' }}
|
||||
select
|
||||
onChange={updateFormValue}
|
||||
>
|
||||
<MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
|
||||
<MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="f"
|
||||
label="Pulse"
|
||||
value={numberValue(editItem.f)}
|
||||
type="number"
|
||||
sx={{ width: '15ch' }}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">s</InputAdornment>
|
||||
)
|
||||
},
|
||||
htmlInput: { min: '0', max: '10000', step: '0.1' }
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
@@ -188,7 +188,8 @@ export enum DeviceValueUOM {
|
||||
VOLTS,
|
||||
MBAR,
|
||||
LH,
|
||||
CTKWH
|
||||
CTKWH,
|
||||
HZ
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
@@ -218,7 +219,8 @@ export const DeviceValueUOM_s = [
|
||||
'V',
|
||||
'mbar',
|
||||
'l/h',
|
||||
'ct/kWh'
|
||||
'ct/kWh',
|
||||
'Hz'
|
||||
];
|
||||
|
||||
export enum AnalogType {
|
||||
@@ -234,7 +236,11 @@ export enum AnalogType {
|
||||
PWM_1 = 8,
|
||||
PWM_2 = 9,
|
||||
NTC = 10,
|
||||
RGB = 11
|
||||
RGB = 11,
|
||||
PULSE = 12,
|
||||
FREQ_0 = 13,
|
||||
FREQ_1 = 14,
|
||||
FREQ_2 = 15
|
||||
}
|
||||
|
||||
export const AnalogTypeNames = [
|
||||
@@ -249,7 +255,11 @@ export const AnalogTypeNames = [
|
||||
'PWM 1',
|
||||
'PWM 2',
|
||||
'NTC Temp.',
|
||||
'RGB Led'
|
||||
'RGB Led',
|
||||
'Pulse',
|
||||
'Freq 0',
|
||||
'Freq 1',
|
||||
'Freq 2'
|
||||
];
|
||||
|
||||
type BoardProfiles = Record<string, string>;
|
||||
|
||||
@@ -33,9 +33,9 @@ void MqttSettingsService::begin() {
|
||||
|
||||
void MqttSettingsService::startClient() {
|
||||
static bool isSecure = false;
|
||||
if (_mqttClient != nullptr) {
|
||||
if (_mqttClient) {
|
||||
// do we need to change the client?
|
||||
if (_state.enabled && ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS))) {
|
||||
if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) {
|
||||
return;
|
||||
}
|
||||
delete _mqttClient;
|
||||
@@ -79,9 +79,6 @@ void MqttSettingsService::startClient() {
|
||||
}
|
||||
|
||||
void MqttSettingsService::loop() {
|
||||
if (!_state.enabled || _mqttClient == nullptr || emsesp::EMSESP::system_.systemStatus() != 0) {
|
||||
return;
|
||||
}
|
||||
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||
// reconfigure MQTT client
|
||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||
|
||||
@@ -23,6 +23,31 @@ namespace emsesp {
|
||||
|
||||
uuid::log::Logger AnalogSensor::logger_{F_(analogsensor), uuid::log::Facility::DAEMON};
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
unsigned long AnalogSensor::edge[] = {0, 0, 0};
|
||||
unsigned long AnalogSensor::edgecnt[] = {0, 0, 0};
|
||||
|
||||
void IRAM_ATTR AnalogSensor::freqIrq0() {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
edgecnt[0]++;
|
||||
edge[0] = micros();
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
void IRAM_ATTR AnalogSensor::freqIrq1() {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
edgecnt[1]++;
|
||||
edge[1] = micros();
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
void IRAM_ATTR AnalogSensor::freqIrq2() {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
edgecnt[2]++;
|
||||
edge[2] = micros();
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnalogSensor::start(const bool factory_settings) {
|
||||
// if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0")) {
|
||||
if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||
@@ -92,7 +117,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
for (const auto & sensor : settings.analogCustomizations) { // search customlist
|
||||
if (sensor_.gpio() == sensor.gpio) {
|
||||
// for output sensors set value to new start-value
|
||||
if (sensor.type >= AnalogType::DIGITAL_OUT
|
||||
if (sensor.type >= AnalogType::DIGITAL_OUT && sensor.type <= AnalogType::PWM_2
|
||||
&& (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) {
|
||||
sensor_.set_value(sensor.offset);
|
||||
}
|
||||
@@ -134,7 +159,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
}
|
||||
}
|
||||
if (sensor.type == AnalogType::COUNTER || (sensor.type >= AnalogType::DIGITAL_OUT && sensor.type <= AnalogType::PWM_2)
|
||||
|| sensor.type == AnalogType::RGB) {
|
||||
|| sensor.type == AnalogType::RGB || sensor.type == AnalogType::PULSE) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||
sensor.name.c_str(),
|
||||
@@ -142,6 +167,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
sensor.type == AnalogType::COUNTER ? FL_(counter)
|
||||
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
||||
: sensor.type == AnalogType::RGB ? FL_(RGB)
|
||||
: sensor.type == AnalogType::PULSE ? FL_(pulse)
|
||||
: FL_(pwm),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
@@ -204,6 +230,18 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
sensor.set_offset(0);
|
||||
sensor.set_value(0);
|
||||
publish_sensor(sensor);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
} else if (sensor.type() >= AnalogType::FREQ_0 && sensor.type() <= AnalogType::FREQ_2) {
|
||||
LOG_DEBUG("Frequency on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
sensor.set_offset(0);
|
||||
sensor.set_value(0);
|
||||
publish_sensor(sensor);
|
||||
auto index = sensor.type() - AnalogType::FREQ_0;
|
||||
attachInterrupt(sensor.gpio(), index == 0 ? freqIrq0 : index == 1 ? freqIrq1 : freqIrq2, FALLING);
|
||||
lastedge[index] = edge[index] = micros();
|
||||
edgecnt[index] = 0;
|
||||
#endif
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
LOG_DEBUG("Digital Read on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
@@ -260,6 +298,11 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
sensor.set_value(sensor.offset());
|
||||
}
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::PULSE) {
|
||||
LOG_DEBUG("Pulse on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
digitalWrite(sensor.gpio(), (sensor.offset() == 1) ^ (sensor.value() == 1));
|
||||
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
@@ -330,6 +373,25 @@ void AnalogSensor::measure() {
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
#ifndef EMSESP_STANDALONE
|
||||
} else if (sensor.type() >= AnalogType::FREQ_0 && sensor.type() <= AnalogType::FREQ_2) {
|
||||
auto index = sensor.type() - AnalogType::FREQ_0;
|
||||
auto oldval = sensor.value();
|
||||
if (edge[index] != lastedge[index] && edgecnt[index] > 0) {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
auto t = (edge[index] - lastedge[index]) / edgecnt[index];
|
||||
lastedge[index] = edge[index];
|
||||
edgecnt[index] = 0;
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
sensor.set_value(sensor.factor() * 1000000.0 / t);
|
||||
} else if (micros() - edge[index] > 10000000ul && sensor.value() > 0) {
|
||||
sensor.set_value(0);
|
||||
}
|
||||
if (sensor.value() != oldval) {
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,9 +399,9 @@ void AnalogSensor::measure() {
|
||||
// poll digital io every time with debounce
|
||||
// go through the list of digital sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER
|
||||
|| sensor.type() == AnalogType::RATE) {
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
auto current_reading = digitalRead(sensor.gpio());
|
||||
if (sensor.poll_ != current_reading) { // check for pinchange
|
||||
sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
|
||||
@@ -362,13 +424,17 @@ void AnalogSensor::measure() {
|
||||
sensor.last_polltime_ = sensor.polltime_;
|
||||
}
|
||||
}
|
||||
|
||||
// see if there is a change and increment # reads
|
||||
if (old_value != sensor.value()) {
|
||||
sensorreads_++;
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
if (sensor.type() == AnalogType::PULSE && sensor.value() && sensor.polltime_ && sensor.polltime_ < uuid::get_uptime()) {
|
||||
sensor.set_value(0);
|
||||
digitalWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.polltime_ = 0;
|
||||
}
|
||||
// see if there is a change and increment # reads
|
||||
if (old_value != sensor.value()) {
|
||||
sensorreads_++;
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,6 +628,9 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
case AnalogType::PWM_0:
|
||||
case AnalogType::PWM_1:
|
||||
case AnalogType::PWM_2:
|
||||
case AnalogType::FREQ_0:
|
||||
case AnalogType::FREQ_1:
|
||||
case AnalogType::FREQ_2:
|
||||
case AnalogType::RGB:
|
||||
case AnalogType::NTC:
|
||||
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double
|
||||
@@ -762,7 +831,7 @@ void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["analog"] = FL_(list_sensortype)[sensor.type()];
|
||||
output["value"] = sensor.value();
|
||||
output["readable"] = true;
|
||||
output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() >= AnalogType::RGB
|
||||
output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE
|
||||
|| (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2);
|
||||
output["visible"] = true;
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
@@ -842,6 +911,12 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b);
|
||||
#endif
|
||||
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
||||
} else if (sensor.type() == AnalogType::PULSE) {
|
||||
uint8_t v = val;
|
||||
sensor.set_value(v);
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
digitalWrite(sensor.gpio(), (sensor.offset() != 0) ^ (sensor.value() != 0));
|
||||
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
uint8_t v = val;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
@@ -124,7 +124,11 @@ class AnalogSensor {
|
||||
PWM_1 = 8,
|
||||
PWM_2 = 9,
|
||||
NTC = 10,
|
||||
RGB = 11
|
||||
RGB = 11,
|
||||
PULSE = 12,
|
||||
FREQ_0 = 13,
|
||||
FREQ_1 = 14,
|
||||
FREQ_2 = 15
|
||||
};
|
||||
|
||||
void start(const bool factory_settings = false);
|
||||
@@ -190,6 +194,15 @@ class AnalogSensor {
|
||||
bool changed_ = true; // this will force a publish of all sensors when initialising
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static void IRAM_ATTR freqIrq0();
|
||||
static void IRAM_ATTR freqIrq1();
|
||||
static void IRAM_ATTR freqIrq2();
|
||||
static unsigned long edge[3];
|
||||
static unsigned long edgecnt[3];
|
||||
unsigned long lastedge[3] = {0, 0, 0};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -364,6 +364,10 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
// EMS device commands
|
||||
//
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
EMSESP::scan_devices();
|
||||
});
|
||||
/* removed scan deep
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, {F_(deep_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
EMSESP::scan_devices();
|
||||
@@ -379,7 +383,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
// read <deviceID> <type ID> [offset] [length]
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
// Boilers - 0x08
|
||||
{ 8, DeviceType::BOILER, "CS5800i, CS6800i, WLW176i, WLW186i", DeviceFlags::EMS_DEVICE_FLAG_CS6800},
|
||||
{ 11, DeviceType::BOILER, "Greenstar 2000", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 12, DeviceType::BOILER, "C1200W", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 16, DeviceType::BOILER, "CS5800iG", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 64, DeviceType::BOILER, "BK13/BK15, Smartline, GB1*2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
@@ -258,6 +258,9 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(pool))) {
|
||||
return DeviceType::POOL;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(connect))) {
|
||||
return DeviceType::CONNECT;
|
||||
}
|
||||
|
||||
// non EMS
|
||||
if (!strcmp(lowtopic, F_(custom))) {
|
||||
@@ -1896,6 +1899,11 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
create_device_config = false; // only create the main config once
|
||||
count++;
|
||||
}
|
||||
// SRC thermostats mapped to connect/hs1/...
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HS1 && !strcmp(dv.short_name, FL_(roomtemp)[0])) {
|
||||
Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max);
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// always create minimum one config
|
||||
if (count && (heap_caps_get_free_size(MALLOC_CAP_8BIT) < 65 * 1024)) { // checks free Heap+PSRAM
|
||||
|
||||
@@ -108,10 +108,10 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
const char * DeviceValue::DeviceValueUOM_s[] = {
|
||||
|
||||
F_(uom_blank), // 0
|
||||
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0],
|
||||
FL_(minutes)[0], F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0],
|
||||
F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin),
|
||||
F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_lh), F_(uom_ctkwh), F_(uom_blank)
|
||||
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
|
||||
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit),
|
||||
F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar),
|
||||
F_(uom_lh), F_(uom_ctkwh), F_(uom_hz), F_(uom_blank)
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ class DeviceValue {
|
||||
MBAR, // 24 - mbar
|
||||
LH, // 25 - l/h
|
||||
CTKWH, // 26 - ct/kWh
|
||||
CONNECTIVITY // 27 - used in HA
|
||||
HZ, // 27 - Hz
|
||||
CONNECTIVITY // 28 - used in HA
|
||||
};
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp
|
||||
|
||||
@@ -1307,7 +1307,11 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
default_name = "Wireless sensor base";
|
||||
}
|
||||
}
|
||||
|
||||
// map MX400 also to RF Base
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_RFBASE) {
|
||||
device_type = DeviceType::CONNECT;
|
||||
default_name = "Wireless base";
|
||||
}
|
||||
if ((device_id >= EMSdevice::EMS_DEVICE_ID_DHW1 && device_id <= EMSdevice::EMS_DEVICE_ID_DHW8) || device_id == EMSdevice::EMS_DEVICE_ID_IPM_DHW) {
|
||||
device_type = DeviceType::WATER;
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ MAKE_WORD_CUSTOM(uom_volts, "V")
|
||||
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
|
||||
MAKE_WORD_CUSTOM(uom_lh, "l/h")
|
||||
MAKE_WORD_CUSTOM(uom_ctkwh, "ct/kWh")
|
||||
MAKE_WORD_CUSTOM(uom_hz, "Hz")
|
||||
|
||||
// MQTT topics and prefixes
|
||||
MAKE_WORD_CUSTOM(heating_active, "heating_active")
|
||||
@@ -277,7 +278,8 @@ MAKE_ENUM_FIXED(list_syslog_level, "off", "emerg", "alert", "crit", "error", "wa
|
||||
MAKE_ENUM_FIXED(counter, "counter")
|
||||
MAKE_ENUM_FIXED(digital_out, "digital_out")
|
||||
MAKE_ENUM_FIXED(RGB, "RGB")
|
||||
MAKE_ENUM_FIXED(list_sensortype, "disabled", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2", "NTC Temp", "RGB Led")
|
||||
MAKE_ENUM_FIXED(pulse, "pulse")
|
||||
MAKE_ENUM_FIXED(list_sensortype, "disabled", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2", "NTC Temp", "RGB Led", "pulse", "freq 0", "freq 1", "freq 2")
|
||||
|
||||
// watch
|
||||
MAKE_ENUM_FIXED(list_watch, "off", "on", "raw", "unknown")
|
||||
@@ -337,6 +339,7 @@ MAKE_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_ENUM(enum_mode7, FL_(off), FL_(manual)) // CR11
|
||||
MAKE_ENUM(enum_mode8, FL_(auto), FL_(heat), FL_(cool), FL_(off)) // SRC room thermostats
|
||||
MAKE_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
|
||||
MAKE_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
|
||||
@@ -915,6 +915,9 @@ MAKE_TRANSLATION(status, "status", "status", "Status", "Status", "Status", "stat
|
||||
// RF sensor, id 0x40, telegram 0x435
|
||||
MAKE_TRANSLATION(RFTemp, "rftemp", "RF room temperature sensor", "RF Raumtemperatursensor", "RF ruimtetemperatuur sensor", "RF Rumsgivare Temperatur", "bezprzewodowy czujnik temperatury pomieszczenia", "RF romsgiver temp", "capteur de température de pièce RF", "RF oda sıcaklık sensörü", "Sensore di temperatura ambiente RF", "RF snímač izbovej teploty", "RF senzor teploty místnosti")
|
||||
|
||||
// gateway thermostat
|
||||
MAKE_TRANSLATION(name, "name", "name", "Name")
|
||||
|
||||
// ventilation
|
||||
MAKE_TRANSLATION(outFresh, "outfresh", "outdoor fresh air", "Außenlufttemp.", "temperatuur buitenlucht", "Utelufttemperatur", "świeże powietrze z zewnątrz", "", "", "dış ortam taze hava", "aria fresca esterna", "čerstvý vzduch vonku", "venkovní čerstvý vzduch") // TODO translate
|
||||
MAKE_TRANSLATION(inFresh, "infresh", "indoor fresh air", "Zulufttemp.", "temperatuur aanvoer", "Tillufttemperatur", "nawiew", "", "", "iç ortam taze hava", "aria fresca interna", "čerstvý vzduch v interiéri", "vnitřní čerstvý vzduch") // TODO translate
|
||||
|
||||
@@ -528,8 +528,8 @@ void Mqtt::ha_status() {
|
||||
strcpy(uniq, "system_status");
|
||||
}
|
||||
|
||||
doc["uniq_id"] = uniq;
|
||||
doc["obj_id"] = uniq;
|
||||
doc["uniq_id"] = uniq;
|
||||
doc["default_entity_id"] = uniq;
|
||||
|
||||
doc["stat_t"] = Mqtt::base() + "/status";
|
||||
doc["name"] = "System status";
|
||||
@@ -827,7 +827,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s", entity);
|
||||
}
|
||||
|
||||
// build unique identifier also used as object_id which also becomes the Entity ID in HA
|
||||
// build unique identifier also used as default_entity_id which also becomes the Entity ID in HA
|
||||
char uniq_id[80];
|
||||
|
||||
// list of boiler entities that need conversion for 3.6 compatibility, add ww suffix
|
||||
@@ -980,8 +980,8 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
|
||||
// build the full payload
|
||||
JsonDocument doc;
|
||||
doc["uniq_id"] = uniq_id;
|
||||
doc["obj_id"] = uniq_id; // same as unique_id
|
||||
doc["uniq_id"] = uniq_id;
|
||||
doc["default_entity_id"] = uniq_id; // same as unique_id
|
||||
|
||||
char sample_val[30] = "0"; // sample, correct(!) entity value, used only to prevent warning/error in HA if real value is not published yet
|
||||
|
||||
@@ -1235,7 +1235,9 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
}
|
||||
|
||||
bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint32_t max) {
|
||||
uint8_t hc_num = tag;
|
||||
uint8_t hc_num = tag < DeviceValueTAG::TAG_HS1 ? tag : tag - DeviceValueTAG::TAG_HS1 + 1;
|
||||
const char * devicename = tag < DeviceValueTAG::TAG_HS1 ? "thermostat" : "connect";
|
||||
const char * tagname = tag < DeviceValueTAG::TAG_HS1 ? "hc" : "hs";
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
@@ -1253,21 +1255,21 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
|
||||
char min_s[10];
|
||||
char max_s[10];
|
||||
|
||||
snprintf(topic, sizeof(topic), "climate/%s/thermostat_hc%d/config", Mqtt::basename().c_str(), hc_num);
|
||||
snprintf(topic, sizeof(topic), "climate/%s/%s_%s%d/config", Mqtt::basename().c_str(), devicename, tagname, hc_num);
|
||||
if (remove) {
|
||||
return queue_remove_topic(topic); // publish empty payload with retain flag
|
||||
}
|
||||
|
||||
if (Mqtt::is_nested()) {
|
||||
// nested format
|
||||
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.hc%d.mode", hc_num);
|
||||
snprintf(hc_mode_cond, sizeof(hc_mode_cond), "value_json.hc%d is undefined or %s is undefined", hc_num, hc_mode_s);
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.hc%d.seltemp", hc_num);
|
||||
snprintf(seltemp_cond, sizeof(seltemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, seltemp_s);
|
||||
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.%s%d.mode", tagname, hc_num);
|
||||
snprintf(hc_mode_cond, sizeof(hc_mode_cond), "value_json.%s%d is undefined or %s is undefined", tagname, hc_num, hc_mode_s);
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.%s%d.seltemp", tagname, hc_num);
|
||||
snprintf(seltemp_cond, sizeof(seltemp_cond), "value_json.%s%d is defined and %s is defined", tagname, hc_num, seltemp_s);
|
||||
|
||||
if (has_roomtemp) {
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.hc%d.currtemp", hc_num);
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, currtemp_s);
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.%s%d.currtemp", tagname, hc_num);
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "value_json.%s%d is defined and %s is defined", tagname, hc_num, currtemp_s);
|
||||
}
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
|
||||
} else {
|
||||
@@ -1281,7 +1283,10 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.currtemp");
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "%s is defined", currtemp_s);
|
||||
}
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_HC1 + hc_num - 1).c_str());
|
||||
snprintf(topic_t,
|
||||
sizeof(topic_t),
|
||||
"~/%s",
|
||||
Mqtt::tag_to_topic(tag < DeviceValueTAG::TAG_HS1 ? EMSdevice::DeviceType::THERMOSTAT : EMSdevice::DeviceType::CONNECT, tag).c_str());
|
||||
}
|
||||
|
||||
snprintf(mode_str_tpl,
|
||||
@@ -1297,28 +1302,28 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
|
||||
hc_mode_s,
|
||||
Helpers::translated_word(FL_(off)));
|
||||
|
||||
snprintf(name_s, sizeof(name_s), "Hc%d", hc_num);
|
||||
snprintf(name_s, sizeof(name_s), "%s%d", tag < DeviceValueTAG::TAG_HS1 ? "Hc" : "Hs", hc_num);
|
||||
|
||||
if (Mqtt::entity_format() == entityFormat::MULTI_SHORT) {
|
||||
snprintf(uniq_id_s, sizeof(uniq_id_s), "%s_thermostat_hc%d", Mqtt::basename().c_str(), hc_num); // add basename
|
||||
snprintf(uniq_id_s, sizeof(uniq_id_s), "%s_%s%s%d", Mqtt::basename().c_str(), devicename, tagname, hc_num); // add basename
|
||||
} else {
|
||||
snprintf(uniq_id_s, sizeof(uniq_id_s), "thermostat_hc%d", hc_num); // backward compatible with v3.4
|
||||
snprintf(uniq_id_s, sizeof(uniq_id_s), "%s%d", devicename, hc_num); // backward compatible with v3.4
|
||||
}
|
||||
|
||||
snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num);
|
||||
snprintf(mode_cmd_s, sizeof(mode_cmd_s), "~/thermostat/hc%d/mode", hc_num);
|
||||
snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/%s/%s%d/seltemp", devicename, tagname, hc_num);
|
||||
snprintf(mode_cmd_s, sizeof(mode_cmd_s), "~/%s/%s%d/mode", devicename, tagname, hc_num);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
doc["~"] = Mqtt::base();
|
||||
doc["uniq_id"] = uniq_id_s;
|
||||
doc["obj_id"] = uniq_id_s; // same as uniq_id
|
||||
doc["name"] = name_s;
|
||||
doc["mode_stat_t"] = topic_t;
|
||||
doc["mode_stat_tpl"] = mode_str_tpl;
|
||||
doc["temp_cmd_t"] = temp_cmd_s;
|
||||
doc["temp_stat_t"] = topic_t;
|
||||
doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 0}}";
|
||||
doc["~"] = Mqtt::base();
|
||||
doc["uniq_id"] = uniq_id_s;
|
||||
doc["default_entity_id"] = uniq_id_s; // same as uniq_id
|
||||
doc["name"] = name_s;
|
||||
doc["mode_stat_t"] = topic_t;
|
||||
doc["mode_stat_tpl"] = mode_str_tpl;
|
||||
doc["temp_cmd_t"] = temp_cmd_s;
|
||||
doc["temp_stat_t"] = topic_t;
|
||||
doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 0}}";
|
||||
|
||||
if (has_roomtemp) {
|
||||
doc["curr_temp_t"] = topic_t;
|
||||
@@ -1341,7 +1346,7 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
|
||||
modes.add("heat");
|
||||
modes.add("off");
|
||||
|
||||
add_ha_dev_section(doc.as<JsonObject>(), "thermostat", nullptr, nullptr, nullptr, false); // add dev section
|
||||
add_ha_dev_section(doc.as<JsonObject>(), devicename, nullptr, nullptr, nullptr, false); // add dev section
|
||||
add_ha_avail_section(doc.as<JsonObject>(), topic_t, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); // add availability section
|
||||
|
||||
return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
@@ -518,7 +518,7 @@ std::string calculate(const std::string & expr) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
stack.push_back(to_string(rhd * esp_random() / UINT32_MAX));
|
||||
#else
|
||||
stack.push_back(to_string(rhd * random()));
|
||||
stack.push_back(to_string(rhd * rand() / RAND_MAX));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0xE9, "UBAMonitorWWPlus", false, MAKE_PF_CB(process_UBAMonitorWWPlus));
|
||||
register_telegram_type(0xEA, "UBAParameterWWPlus", true, MAKE_PF_CB(process_UBAParameterWWPlus));
|
||||
register_telegram_type(0x28, "WeatherComp", true, MAKE_PF_CB(process_WeatherComp));
|
||||
register_telegram_type(0x2E0, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints2));
|
||||
}
|
||||
|
||||
if (isHeatPump()) {
|
||||
@@ -1208,8 +1209,13 @@ void Boiler::check_active() {
|
||||
static uint32_t lastSendHeatingOff = 0;
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && (uuid::get_uptime_sec() - lastSendHeatingOff) >= 60) {
|
||||
lastSendHeatingOff = uuid::get_uptime_sec();
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
if (has_telegram_id(0xE4)) {
|
||||
uint8_t data[] = {1, 0, 0, 1, 1};
|
||||
write_command(EMS_TYPE_UBASetPoints2, 0, data, sizeof(data), 0);
|
||||
} else {
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate energy for boiler 0x08 from stored modulation an time in units of 0.01 Wh
|
||||
@@ -1832,20 +1838,34 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t setFlowTemp_ = 0;
|
||||
uint8_t setBurnPow_ = 0;
|
||||
uint8_t wwSetBurnPow_ = 0;
|
||||
uint8_t setFlowTemp_ = 0;
|
||||
uint8_t setBurnPow_ = 0;
|
||||
uint8_t setPumpMod_ = 0;
|
||||
telegram->read_value(setFlowTemp_, 0);
|
||||
telegram->read_value(setBurnPow_, 1);
|
||||
telegram->read_value(wwSetBurnPow_, 2);
|
||||
telegram->read_value(setPumpMod_, 2);
|
||||
|
||||
// overwrite other settings on receive?
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08 && (setFlowTemp_ + setBurnPow_ + wwSetBurnPow_) != 0) {
|
||||
// forceHeatingOff overwrite to zero
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08 && (setFlowTemp_ + setBurnPow_ + setPumpMod_) != 0) {
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// UBASetPoints ems+ 0x2E0
|
||||
void Boiler::process_UBASetPoints2(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t setFlowTemp_ = 0;
|
||||
uint8_t setBurnPow_ = 0;
|
||||
telegram->read_value(setFlowTemp_, 0);
|
||||
telegram->read_value(setBurnPow_, 1);
|
||||
|
||||
// forceHeatingOff overwrite to zero
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08 && (setFlowTemp_ + setBurnPow_) != 0) {
|
||||
uint8_t data[] = {1, 0, 0, 1, 1};
|
||||
write_command(EMS_TYPE_UBASetPoints2, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x35 - not yet implemented, not readable, only for settings
|
||||
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
@@ -2419,12 +2439,17 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
// no verify if value is unchanged, put it to end of tx-queue, no priority
|
||||
// see https://github.com/emsesp/EMS-ESP32/issues/654, https://github.com/emsesp/EMS-ESP32/issues/954
|
||||
if (v == selFlowTemp_) {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *)&v, 1, 0, false);
|
||||
uint8_t v1 = v;
|
||||
if (has_telegram_id(0xE4)) {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints2, 1, &v1, 1, 0, false);
|
||||
} else {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, &v1, 1, 0, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (has_telegram_id(0xE4)) {
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, v, 0xE4);
|
||||
write_command(EMS_TYPE_UBASetPoints2, 1, v, 0xE4);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, v, 0x18);
|
||||
}
|
||||
@@ -2438,8 +2463,11 @@ bool Boiler::set_burn_power(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(EMS_TYPE_UBASetPoints, 1, v, EMS_TYPE_UBASetPoints);
|
||||
|
||||
if (has_telegram_id(0xE4)) {
|
||||
write_command(EMS_TYPE_UBASetPoints2, 2, v);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBASetPoints, 1, v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3415,14 +3443,20 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
|
||||
bool Boiler::set_forceHeatingOff(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (Helpers::value2bool(value, v)) {
|
||||
has_update(forceHeatingOff_, v);
|
||||
if (!v && Helpers::hasValue(heatingTemp_)) {
|
||||
uint8_t data[] = {heatingTemp_,
|
||||
(Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100),
|
||||
(Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)0),
|
||||
0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
// set only on change on->off
|
||||
if (!v && forceHeatingOff_ && Helpers::hasValue(heatingTemp_)) {
|
||||
if (has_telegram_id(0xE4)) {
|
||||
uint8_t data[] = {1, heatingTemp_, (Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100), 1, 1};
|
||||
write_command(EMS_TYPE_UBASetPoints2, 0, data, sizeof(data), 0);
|
||||
} else {
|
||||
uint8_t data[] = {heatingTemp_,
|
||||
(Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100),
|
||||
(Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)100),
|
||||
0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
has_update(forceHeatingOff_, v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -354,6 +354,7 @@ class Boiler : public EMSdevice {
|
||||
void process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBASetPoints2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAFlags(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MC110Status(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
@@ -32,6 +32,18 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(outdoorTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
// Roomthermostats
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
register_telegram_type(0x0BDD + i, "Room", false, MAKE_PF_CB(process_roomThermostat)); // broadcasted
|
||||
register_telegram_type(0x0B3D + i, "Roomname", false, MAKE_PF_CB(process_roomThermostatName)); // fetch for active circuits
|
||||
register_telegram_type(0x0BB5 + i, "Roomsettings", false, MAKE_PF_CB(process_roomThermostatMode)); // fetch for active circuits
|
||||
register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits
|
||||
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted
|
||||
}
|
||||
// register_telegram_type(0xDB65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule));
|
||||
// 0x2040, broadcast 36 bytes:
|
||||
// data: 0E 60 00 DF 0D AF 0A 46 0A 46 02 9A 1C 53 1C 53 12 AD 12 AD 00 00 13 C2
|
||||
// data: 1F 37 1F 37 00 00 00 00 18 97 11 27 (offset 24)
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -46,6 +58,151 @@ void Connect::process_OutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
(0x0880), data: 01 04
|
||||
(0x0889), data: 00 80 80 01
|
||||
*/
|
||||
void Connect::register_device_values_room(std::shared_ptr<Connect::RoomCircuit> room) {
|
||||
auto tag = DeviceValueTAG::TAG_HS1 + room->room();
|
||||
register_device_value(tag, &room->temp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(roomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &room->humidity_, DeviceValueType::INT8, FL_(airHumidity), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &room->dewtemp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(dewTemperature), DeviceValueUOM::DEGREES);
|
||||
register_device_value(
|
||||
tag, &room->seltemp_, DeviceValueType::UINT8, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(seltemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_seltemp));
|
||||
register_device_value(tag, &room->mode_, DeviceValueType::ENUM, FL_(enum_mode8), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||
register_device_value(tag, &room->name_, DeviceValueType::STRING, FL_(name), DeviceValueUOM::NONE, MAKE_CF_CB(set_name));
|
||||
}
|
||||
|
||||
std::shared_ptr<Connect::RoomCircuit> Connect::room_circuit(const uint8_t num, const bool create) {
|
||||
// check for existing circuit
|
||||
for (const auto & room_circuit : room_circuits_) {
|
||||
if (room_circuit->room() == num) {
|
||||
return room_circuit;
|
||||
}
|
||||
}
|
||||
if (!create) {
|
||||
return nullptr;
|
||||
}
|
||||
// create a new circuit object and add to the list
|
||||
auto new_room = std::make_shared<Connect::RoomCircuit>(num);
|
||||
room_circuits_.push_back(new_room);
|
||||
// register the device values
|
||||
register_device_values_room(new_room);
|
||||
toggle_fetch(0x0B3D + num, true); // name
|
||||
toggle_fetch(0x0BB5 + num, true); // mode
|
||||
toggle_fetch(0x1230 + num, true); // unknown
|
||||
|
||||
return new_room; // return back point to new HC object
|
||||
}
|
||||
|
||||
// gateway(0x50) B all(0x00), ?(0x0BDD), data: 00 E6 36 2A
|
||||
void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
|
||||
bool create = telegram->offset == 0 && telegram->message_data[0] < 0x80;
|
||||
auto rc = room_circuit(telegram->type_id - 0xBDD, create);
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
has_update(telegram, rc->temp_, 0);
|
||||
has_update(telegram, rc->humidity_, 2); // could show -3 if not set
|
||||
has_update(telegram, rc->seltemp_, 3);
|
||||
|
||||
// calculate dew temperature
|
||||
const float k2 = 17.62;
|
||||
const float k3 = 243.12;
|
||||
const float t = (float)rc->temp_ / 10;
|
||||
const float h = (float)rc->humidity_ / 100;
|
||||
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
|
||||
has_update(rc->dewtemp_, dt);
|
||||
}
|
||||
|
||||
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01
|
||||
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 00 4B 00 FC 00 63 00 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (offset 1)
|
||||
void Connect::process_roomThermostatName(std::shared_ptr<const Telegram> telegram) {
|
||||
auto rc = room_circuit(telegram->type_id - 0xB3D);
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = telegram->offset; i < telegram->message_length + telegram->offset && i < 100; i++) {
|
||||
if ((i > 1) && (i % 2) == 0) {
|
||||
rc->name_[(i - 2) / 2] = telegram->message_data[i];
|
||||
}
|
||||
}
|
||||
rc->name_[50] = '\0'; // make sure name is terminated
|
||||
}
|
||||
|
||||
// settings 0-mode, 1-tempautotemp, 3 - manualtemp, 6,7 - ?
|
||||
void Connect::process_roomThermostatMode(std::shared_ptr<const Telegram> telegram) {
|
||||
auto rc = room_circuit(telegram->type_id - 0xBB5);
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
// has_enumupdate(telegram, rc->mode_, 0, {3, 1, 0});
|
||||
has_update(telegram, rc->mode_, 0);
|
||||
}
|
||||
|
||||
// unknown telegrams, needs fetch
|
||||
void Connect::process_roomThermostatParam(std::shared_ptr<const Telegram> telegram) {
|
||||
auto rc = room_circuit(telegram->type_id - 0x1230);
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown broadcasted telegrams
|
||||
void Connect::process_roomThermostatData(std::shared_ptr<const Telegram> telegram) {
|
||||
auto rc = room_circuit(telegram->type_id - 0x1244);
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Settings:
|
||||
|
||||
bool Connect::set_mode(const char * value, const int8_t id) {
|
||||
auto rc = room_circuit(id - DeviceValueTAG::TAG_HS1);
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
uint8_t v;
|
||||
// if (Helpers::value2enum(value, v, FL_(enum_mode2), {3, 1, 0})) {
|
||||
if (Helpers::value2enum(value, v, FL_(enum_mode8))) {
|
||||
write_command(0xBB5 + rc->room(), 0, v); // no validate, mode change is broadcasted
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Connect::set_seltemp(const char * value, const int8_t id) {
|
||||
auto rc = room_circuit(id - DeviceValueTAG::TAG_HS1);
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
float v;
|
||||
if (Helpers::value2float(value, v)) {
|
||||
// write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
write_command(0xBB5 + rc->room(), rc->mode_ == 0 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Connect::set_name(const char * value, const int8_t id) {
|
||||
auto rc = room_circuit(id - DeviceValueTAG::TAG_HS1);
|
||||
if (rc == nullptr || value == nullptr || strlen(value) > 50) {
|
||||
return false;
|
||||
}
|
||||
uint8_t len = strlen(value) * 2 + 2;
|
||||
uint8_t data[len];
|
||||
for (uint8_t i = 0; i < strlen(value) + 1; i++) { // include terminating '\0'
|
||||
data[2 * i] = 0;
|
||||
data[2 * i + 1] = value[i];
|
||||
}
|
||||
uint8_t ofs = 0;
|
||||
while (len > 0) {
|
||||
uint8_t part = len > 25 ? 25 : len;
|
||||
write_command(0x0B3D + rc->room(), ofs + 1, &data[ofs], part, 0);
|
||||
ofs += part;
|
||||
len -= part;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -27,7 +27,42 @@ class Connect : public EMSdevice {
|
||||
public:
|
||||
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
|
||||
|
||||
class RoomCircuit {
|
||||
public:
|
||||
RoomCircuit(const uint8_t num)
|
||||
: room_(num) {
|
||||
}
|
||||
~RoomCircuit() = default;
|
||||
int16_t temp_;
|
||||
int8_t humidity_;
|
||||
uint8_t seltemp_;
|
||||
uint8_t mode_;
|
||||
char name_[51];
|
||||
int16_t dewtemp_;
|
||||
|
||||
uint8_t room() {
|
||||
return room_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t room_; // dhw circuit number 0..10
|
||||
};
|
||||
|
||||
private:
|
||||
std::shared_ptr<Connect::RoomCircuit> room_circuit(const uint8_t num, const bool create = false);
|
||||
|
||||
void register_device_values_room(std::shared_ptr<Connect::RoomCircuit> room);
|
||||
void process_roomThermostat(std::shared_ptr<const Telegram> telegram);
|
||||
void process_roomThermostatName(std::shared_ptr<const Telegram> telegram);
|
||||
void process_roomThermostatMode(std::shared_ptr<const Telegram> telegram);
|
||||
void process_roomThermostatParam(std::shared_ptr<const Telegram> telegram);
|
||||
void process_roomThermostatData(std::shared_ptr<const Telegram> telegram);
|
||||
bool set_mode(const char * value, const int8_t id);
|
||||
bool set_seltemp(const char * value, const int8_t id);
|
||||
bool set_name(const char * value, const int8_t id);
|
||||
|
||||
std::vector<std::shared_ptr<Connect::RoomCircuit>> room_circuits_;
|
||||
|
||||
void process_OutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||
int16_t outdoorTemp_;
|
||||
};
|
||||
|
||||
@@ -2006,12 +2006,12 @@ bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
|
||||
auto t = (int8_t)(ct * 10);
|
||||
LOG_DEBUG("Calibrating internal temperature to %d.%d C", t / 10, t < 0 ? -t % 10 : t % 10);
|
||||
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_RC10) {
|
||||
if (device_id() >= 0x38 && device_id() <= 0x3F) { // remote thermostats RC100H, CR10, ...
|
||||
write_command(0x273 + device_id() - 0x38, 0, t, 0x273 + device_id() - 0x38);
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC10) {
|
||||
write_command(0xB0, 0, t, 0xB0);
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC30) {
|
||||
write_command(EMS_TYPE_RC30Settings, 1, t, EMS_TYPE_RC30Settings);
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100H) {
|
||||
write_command(0x273, 0, t, 0x273);
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||
write_command(0x241, 7, t, 0x241);
|
||||
} else if (isRC300()) {
|
||||
|
||||
@@ -30,7 +30,7 @@ Ventilation::Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product
|
||||
register_telegram_type(0x583, "VentilationMonitor", false, MAKE_PF_CB(process_MonitorMessage));
|
||||
register_telegram_type(0x5D9, "Airquality", false, MAKE_PF_CB(process_VOCMessage));
|
||||
register_telegram_type(0x587, "Bypass", false, MAKE_PF_CB(process_BypassMessage));
|
||||
// register_telegram_type(0x5, "VentilationSet", true, MAKE_PF_CB(process_SetMessage));
|
||||
register_telegram_type(0x55C, "VentilationSet", true, MAKE_PF_CB(process_SetMessage));
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&outFresh_,
|
||||
@@ -50,8 +50,9 @@ Ventilation::Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &bypass_, DeviceValueType::BOOL, FL_(airbypass), DeviceValueUOM::NONE, MAKE_CF_CB(set_bypass));
|
||||
}
|
||||
|
||||
// message
|
||||
// message 0x055C, data: 08 01 11 17
|
||||
void Ventilation::process_SetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, bypass_, 1);
|
||||
}
|
||||
|
||||
// message 583
|
||||
@@ -85,9 +86,10 @@ void Ventilation::process_ModeMessage(std::shared_ptr<const Telegram> telegram)
|
||||
has_enumupdate(telegram, mode_, 0, -1);
|
||||
}
|
||||
|
||||
// message 0x0587, data: 01 00
|
||||
// message 0x0587, data: 00 00 64 00 64 0A 00 01 54 01 00 01 00 00 00 46 00 00 00 02 00 A3 00 A3
|
||||
void Ventilation::process_BypassMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, bypass_, 1);
|
||||
// has_update(telegram, bypass_closing, 0);
|
||||
// has_update(telegram, bypass_opening, 1);
|
||||
}
|
||||
|
||||
bool Ventilation::set_ventMode(const char * value, const int8_t id) {
|
||||
|
||||
@@ -41,7 +41,7 @@ class Ventilation : public EMSdevice {
|
||||
uint8_t ventOutSpeed_;
|
||||
|
||||
// handlers: 0x056B 0x0575 0x0583 0x0585 0x0586 0x0587 0x0588 0x058D 0x058E 0x058F 0x0590 0x05CF 0x05D9 0x05E3
|
||||
void process_SetMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SetMessage(std::shared_ptr<const Telegram> telegram); // 0x55C
|
||||
void process_MonitorMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ModeMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
void process_BlowerMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.7.3-dev.18"
|
||||
#define EMSESP_APP_VERSION "3.7.3-dev.19"
|
||||
|
||||
@@ -436,7 +436,8 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
obj["v"] = Helpers::transformNumFloat(sensor.value());
|
||||
} else
|
||||
#endif
|
||||
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN) {
|
||||
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN
|
||||
|| sensor.type() == AnalogSensor::AnalogType::PULSE) {
|
||||
char s[12];
|
||||
dv["v"] = Helpers::render_boolean(s, sensor.value() != 0, true);
|
||||
JsonArray l = dv["l"].to<JsonArray>();
|
||||
@@ -448,7 +449,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
}
|
||||
if (sensor.type() == AnalogSensor::AnalogType::COUNTER
|
||||
|| (sensor.type() >= AnalogSensor::AnalogType::DIGITAL_OUT && sensor.type() <= AnalogSensor::AnalogType::PWM_2)
|
||||
|| sensor.type() == AnalogSensor::AnalogType::RGB) {
|
||||
|| sensor.type() == AnalogSensor::AnalogType::RGB || sensor.type() == AnalogSensor::AnalogType::PULSE) {
|
||||
dv["c"] = sensor.name();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user