Files
EMS-ESP32/src/devices/connect.cpp
2025-09-30 08:48:56 +02:00

194 lines
7.7 KiB
C++

/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "connect.h"
namespace emsesp {
REGISTER_FACTORY(Connect, EMSdevice::DeviceType::CONNECT);
Connect::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)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
if (device_id == 0x50) { // RF Base
register_telegram_type(0xD1, "RFOutdoorTemp", false, MAKE_PF_CB(process_OutdoorTemp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&outdoorTemp_,
DeviceValueType::INT16,
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));
register_telegram_type(0x0B3D + i, "Roomname", true, MAKE_PF_CB(process_roomThermostatName));
register_telegram_type(0x0BB5 + i, "Roomsettings", true, MAKE_PF_CB(process_roomThermostatMode));
register_telegram_type(0x1230 + i, "Roomparams", true, MAKE_PF_CB(process_roomThermostatParam));
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData));
}
// 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)
}
}
/*
* OutdoorTemp - type 0xD1 - external temperature
*/
void Connect::process_OutdoorTemp(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, outdoorTemp_, 0);
}
/* other values from 0x50 RF base
(0x087F), data: 00 00
(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);
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;
}
if (telegram->offset > 1 || telegram->message_length < 3) {
return;
}
std::string s;
for (uint8_t i = 2 - telegram->offset; (i < telegram->message_length) && (telegram->message_data[i] != 0); i += 2) {
s += (char)telegram->message_data[i];
}
if (s.length()) {
has_update(rc->name_, s.c_str(), s.length() + 1);
}
}
// 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);
}
void Connect::process_roomThermostatParam(std::shared_ptr<const Telegram> telegram) {
}
void Connect::process_roomThermostatData(std::shared_ptr<const Telegram> telegram) {
}
// 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) > 12) {
return false;
}
uint8_t data[strlen(value) * 2];
uint8_t * d = data;
const char * c = value;
while (*c != 0) {
*d++ = 0;
*d++ = *c++;
}
write_command(0x0B3D + rc->room(), 1, data, sizeof(data), 0x0B3D + rc->room());
return true;
}
} // namespace emsesp