mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
product-id 100 is mixer with max wwc10, add handlers_ignored
This commit is contained in:
@@ -113,7 +113,6 @@
|
||||
|
||||
// Solar Modules - 0x30 (for solar), 0x2A, 0x41 (for ww)
|
||||
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10},
|
||||
{100, DeviceType::SOLAR, F("ISM DHW"), DeviceFlags::EMS_DEVICE_FLAG_ISM},
|
||||
{101, DeviceType::SOLAR, F("ISM1"), DeviceFlags::EMS_DEVICE_FLAG_ISM},
|
||||
{103, DeviceType::SOLAR, F("ISM2"), DeviceFlags::EMS_DEVICE_FLAG_ISM},
|
||||
{162, DeviceType::SOLAR, F("SM50"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
@@ -122,6 +121,7 @@
|
||||
|
||||
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
|
||||
{ 69, DeviceType::MIXER, F("MM10"), DeviceFlags::EMS_DEVICE_FLAG_MM10},
|
||||
{100, DeviceType::MIXER, F("IPM"), DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{102, DeviceType::MIXER, F("IPM"), DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{159, DeviceType::MIXER, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{160, DeviceType::MIXER, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
|
||||
@@ -99,17 +99,29 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
|
||||
// HT3
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
register_telegram_type(0x010C, F("IPMStatusMessage"), false, MAKE_PF_CB(process_IPMStatusMessage));
|
||||
register_telegram_type(0x011E, F("IPMTempMessage"), false, MAKE_PF_CB(process_IPMTempMessage));
|
||||
// register_telegram_type(0x0123, F("IPMSetMessage"), false, MAKE_PF_CB(process_IPMSetMessage));
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id - 0x20 + 1;
|
||||
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
|
||||
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
|
||||
if (device_id >= 0x40) { // special DHW pos 10
|
||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_MonitorWW));
|
||||
register_telegram_type(0x1E, F("HydrTemp"), false, MAKE_PF_CB(process_HydrTemp));
|
||||
type_ = Type::WWC;
|
||||
hc_ = device_id - 0x40 + 1;
|
||||
uint8_t tag = DeviceValueTAG::TAG_WWC9 + hc_ - 1;
|
||||
register_device_value(tag, &wwSelTemp_, DeviceValueType::UINT, nullptr, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwSelTemp));
|
||||
register_device_value(tag, &wwCurTemp_1_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &wwCurTemp_2_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &HydrTemp_, DeviceValueType::USHORT, FL_(div10), FL_(hydrTemp), DeviceValueUOM::DEGREES);
|
||||
} else {
|
||||
register_telegram_type(0x010C, F("IPMStatusMessage"), false, MAKE_PF_CB(process_IPMStatusMessage));
|
||||
register_telegram_type(0x011E, F("IPMTempMessage"), false, MAKE_PF_CB(process_IPMTempMessage));
|
||||
// register_telegram_type(0x0123, F("IPMSetMessage"), false, MAKE_PF_CB(process_IPMSetMessage));
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id - 0x20 + 1;
|
||||
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
|
||||
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +215,19 @@ void Mixer::process_MMPLUSConfigMessage_WWC(std::shared_ptr<const Telegram> tele
|
||||
has_update(telegram, wwMaxTemp_, 10);
|
||||
}
|
||||
|
||||
// 0x34
|
||||
void Mixer::process_MonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, wwSelTemp_, 0);
|
||||
has_update(telegram, wwCurTemp_1_, 1);
|
||||
has_update(telegram, wwCurTemp_2_, 3);
|
||||
}
|
||||
// 0x1E
|
||||
void Mixer::process_HydrTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, HydrTemp_, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
@@ -241,6 +266,16 @@ void Mixer::process_IPMSetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
bool Mixer::set_wwSelTemp(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x35, 3, (uint8_t)temperature, 0x34);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Mixer::set_flowSetTemp(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
|
||||
@@ -43,6 +43,9 @@ class Mixer : public EMSdevice {
|
||||
void process_MMSetMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpPoolStatus(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_MonitorWW(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HydrTemp(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
bool set_flowSetTemp(const char * value, const int8_t id);
|
||||
bool set_pump(const char * value, const int8_t id);
|
||||
bool set_activated(const char * value, const int8_t id);
|
||||
@@ -56,6 +59,8 @@ class Mixer : public EMSdevice {
|
||||
bool set_wwCircPump(const char * value, const int8_t id);
|
||||
bool set_wwCircMode(const char * value, const int8_t id);
|
||||
|
||||
bool set_wwSelTemp(const char * value, const int8_t id);
|
||||
|
||||
|
||||
enum class Type {
|
||||
NONE,
|
||||
@@ -90,6 +95,11 @@ class Mixer : public EMSdevice {
|
||||
Type type_ = Type::NONE;
|
||||
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
|
||||
uint8_t poolShuntStatus__ = EMS_VALUE_UINT_NOTSET; // temp value
|
||||
|
||||
uint8_t wwSelTemp_;
|
||||
uint16_t wwCurTemp_1_;
|
||||
uint16_t wwCurTemp_2_;
|
||||
uint16_t HydrTemp_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -63,30 +63,12 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
}
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
if (device_id == 0x41) { // ISM DHW module
|
||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_MonitorWW));
|
||||
} else {
|
||||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, MAKE_PF_CB(process_ISM1StatusMessage));
|
||||
register_telegram_type(0x0101, F("ISM1Set"), true, MAKE_PF_CB(process_ISM1Set));
|
||||
register_telegram_type(0x0104, F("ISM2StatusMessage"), false, MAKE_PF_CB(process_ISM2StatusMessage));
|
||||
}
|
||||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, MAKE_PF_CB(process_ISM1StatusMessage));
|
||||
register_telegram_type(0x0101, F("ISM1Set"), true, MAKE_PF_CB(process_ISM1Set));
|
||||
register_telegram_type(0x0104, F("ISM2StatusMessage"), false, MAKE_PF_CB(process_ISM2StatusMessage));
|
||||
}
|
||||
|
||||
// device values...
|
||||
|
||||
// special case ISM DHW module
|
||||
if (device_id == 0x41) { // ISM DHW module
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||||
&wwSelTemp_,
|
||||
DeviceValueType::UINT,
|
||||
nullptr,
|
||||
FL_(wwSelTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_wwSelTemp));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
|
||||
return;
|
||||
}
|
||||
// special case for a SM100 DHW device_id with 0x2A where it's not actual a solar module
|
||||
if (device_id == 0x2A) {
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp1), DeviceValueUOM::DEGREES);
|
||||
@@ -809,16 +791,6 @@ void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, cylMaxTemp_, 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar DHW Module - type 0x34 ww
|
||||
*/
|
||||
void Solar::process_MonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, wwSelTemp_, 0);
|
||||
has_update(telegram, wwTemp_1_, 1);
|
||||
has_update(telegram, wwTemp_3_, 3);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Settings
|
||||
*/
|
||||
@@ -1163,11 +1135,7 @@ bool Solar::set_wwSelTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
write_command(0x35, 3, (uint8_t)temperature, 0x34);
|
||||
} else { // SM100
|
||||
write_command(0x7A6, 9, (uint8_t)temperature, 0x7A6);
|
||||
}
|
||||
write_command(0x7A6, 9, (uint8_t)temperature, 0x7A6);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,6 @@ class Solar : public EMSdevice {
|
||||
void process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ISM1Set(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ISM2StatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MonitorWW(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
// settings
|
||||
bool set_CollectorMaxTemp(const char * value, const int8_t id);
|
||||
|
||||
@@ -311,6 +311,11 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
shell.printf(F(" Ignored telegram type IDs: "));
|
||||
for (auto handlers : handlers_ignored_) {
|
||||
shell.printf(F("0x%02X "), handlers);
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// list all the telegram type IDs for this device, outputting to a string (max size 200)
|
||||
@@ -333,10 +338,27 @@ char * EMSdevice::show_telegram_handlers(char * result, const size_t len, const
|
||||
strlcat(result, Helpers::hextoa(tf.telegram_type_id_, true).c_str(), len);
|
||||
}
|
||||
}
|
||||
|
||||
if (handlers == Handlers::ALL || handlers == Handlers::IGNORED) {
|
||||
i = 0;
|
||||
for (auto handlers : handlers_ignored_) {
|
||||
if (i++ > 0) {
|
||||
strlcat(result, " ", len);
|
||||
}
|
||||
strlcat(result, Helpers::hextoa(handlers).c_str(), len);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EMSdevice::add_handlers_ignored(const uint16_t handler) {
|
||||
for (auto handlers : handlers_ignored_) {
|
||||
if (handler == handlers) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
handlers_ignored_.push_back(handler);
|
||||
}
|
||||
|
||||
// list all the mqtt handlers for this device
|
||||
void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) const {
|
||||
Mqtt::show_topic_handlers(shell, device_type_);
|
||||
@@ -443,7 +465,7 @@ void EMSdevice::register_device_value(uint8_t tag,
|
||||
|
||||
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
|
||||
flags |= CommandFlag::MQTT_SUB_FLAG_HC;
|
||||
} else if (tag >= DeviceValueTAG::TAG_WWC1 && tag <= DeviceValueTAG::TAG_WWC4) {
|
||||
} else if (tag >= DeviceValueTAG::TAG_WWC1 && tag <= DeviceValueTAG::TAG_WWC10) {
|
||||
flags |= CommandFlag::MQTT_SUB_FLAG_WWC;
|
||||
} else if (tag == DeviceValueTAG::TAG_DEVICE_DATA_WW) {
|
||||
flags |= CommandFlag::MQTT_SUB_FLAG_WW;
|
||||
@@ -517,7 +539,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
if (dv.value_p == value_p && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1 && dv.tag <= DeviceValueTAG::TAG_WWC4) {
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1 && dv.tag <= DeviceValueTAG::TAG_WWC10) {
|
||||
snprintf(topic,
|
||||
sizeof(topic),
|
||||
"%s/%s/%s",
|
||||
@@ -887,7 +909,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
// check if we have hc or wwc
|
||||
if (id >= 1 && id <= 8) {
|
||||
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||
} else if (id >= 9 && id <= 12) {
|
||||
} else if (id >= 9 && id <= 19) {
|
||||
tag = DeviceValueTAG::TAG_WWC1 + id - 9;
|
||||
} else if (id != -1) {
|
||||
return false; // error
|
||||
|
||||
@@ -177,12 +177,13 @@ class EMSdevice {
|
||||
std::string to_string() const;
|
||||
std::string to_string_short() const;
|
||||
|
||||
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING };
|
||||
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING, IGNORED };
|
||||
|
||||
void show_telegram_handlers(uuid::console::Shell & shell) const;
|
||||
char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers);
|
||||
void show_mqtt_handlers(uuid::console::Shell & shell) const;
|
||||
void list_device_entries(JsonObject & output) const;
|
||||
void add_handlers_ignored(const uint16_t handler);
|
||||
|
||||
void mask_entity(const std::string & entity_id);
|
||||
void getMaskedEntities(std::vector<std::string> & entity_ids);
|
||||
@@ -385,6 +386,8 @@ class EMSdevice {
|
||||
|
||||
// device values
|
||||
std::vector<DeviceValue> devicevalues_;
|
||||
|
||||
std::vector<uint16_t> handlers_ignored_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -70,6 +70,12 @@ const __FlashStringHelper * const DeviceValue::DeviceValueTAG_s[] PROGMEM = {
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_wwc5), // "wwc5"
|
||||
F_(tag_wwc6), // "wwc6"
|
||||
F_(tag_wwc7), // "wwc7"
|
||||
F_(tag_wwc8), // "wwc8"
|
||||
F_(tag_wwc9), // "wwc9"
|
||||
F_(tag_wwc10), // "wwc10"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
@@ -109,6 +115,12 @@ const __FlashStringHelper * const DeviceValue::DeviceValueTAG_mqtt[] PROGMEM = {
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_wwc5), // "wwc5"
|
||||
F_(tag_wwc6), // "wwc6"
|
||||
F_(tag_wwc7), // "wwc7"
|
||||
F_(tag_wwc8), // "wwc8"
|
||||
F_(tag_wwc9), // "wwc9"
|
||||
F_(tag_wwc10), // "wwc10"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
|
||||
@@ -90,6 +90,12 @@ class DeviceValue {
|
||||
TAG_WWC2,
|
||||
TAG_WWC3,
|
||||
TAG_WWC4,
|
||||
TAG_WWC5,
|
||||
TAG_WWC6,
|
||||
TAG_WWC7,
|
||||
TAG_WWC8,
|
||||
TAG_WWC9,
|
||||
TAG_WWC10,
|
||||
TAG_HS1,
|
||||
TAG_HS2,
|
||||
TAG_HS3,
|
||||
|
||||
@@ -619,7 +619,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
if (nested) {
|
||||
need_publish |= emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
} else {
|
||||
for (uint8_t hc_tag = DeviceValueTAG::TAG_HC1; hc_tag <= DeviceValueTAG::TAG_WWC4; hc_tag++) {
|
||||
for (uint8_t hc_tag = DeviceValueTAG::TAG_HC1; hc_tag <= DeviceValueTAG::TAG_WWC10; hc_tag++) {
|
||||
json = doc.to<JsonObject>();
|
||||
if (emsdevice->generate_values(json, hc_tag, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
@@ -951,6 +951,9 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
if (wait_validate_ == telegram->type_id) {
|
||||
wait_validate_ = 0;
|
||||
}
|
||||
if (!found && emsdevice->is_device_id(telegram->src) && telegram->message_length > 0) {
|
||||
emsdevice->add_handlers_ignored(telegram->type_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1202,7 +1205,7 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
|
||||
uint8_t tag;
|
||||
if (id >= 1 && id <= 8) {
|
||||
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||
} else if (id >= 9 && id <= 12) {
|
||||
} else if (id >= 9 && id <= 19) {
|
||||
tag = DeviceValueTAG::TAG_WWC1 + id - 9;
|
||||
} else if (id == -1 || id == 0) {
|
||||
tag = DeviceValueTAG::TAG_NONE;
|
||||
|
||||
@@ -1219,6 +1219,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
if (result[0] != '\0') {
|
||||
obj["handlers pending"] = result;
|
||||
}
|
||||
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::IGNORED);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlers ignored"] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user