mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
set wifi replaced
This commit is contained in:
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [1.5.7 dev] 2019-03-
|
## [1.6.0 dev] 2019-03-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- included various fixes and suggestions from @nomis
|
- included various fixes and suggestions from @nomis
|
||||||
- upgraded MyESP library
|
- upgraded MyESP library
|
||||||
- test_mode renamed to silent_mode
|
- test_mode renamed to silent_mode
|
||||||
|
- 'set wifi' replaced with 'set wifi_ssid and set wifi_password' to allow values with spaces
|
||||||
|
|
||||||
## [1.5.6] 2019-03-09
|
## [1.5.6] 2019-03-09
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ EMS-ESP is a project to build an electronic controller circuit using an Espressi
|
|||||||
There are 3 parts to this project, first the design of the circuit, secondly the code for the ESP8266 microcontroller firmware with telnet and MQTT support, and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via a MQTT broker.
|
There are 3 parts to this project, first the design of the circuit, secondly the code for the ESP8266 microcontroller firmware with telnet and MQTT support, and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via a MQTT broker.
|
||||||
|
|
||||||
[](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings)
|
[](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings)
|
||||||
[](CHANGELOG.md)
|
[](CHANGELOG.md)
|
||||||
|
|
||||||
- [EMS-ESP](#ems-esp)
|
- [EMS-ESP](#ems-esp)
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
@@ -69,7 +69,7 @@ The code and circuit has been tested with a few ESP8266 development boards such
|
|||||||
5. Connect an external USB 5v power adapter to the ESP8266 board.
|
5. Connect an external USB 5v power adapter to the ESP8266 board.
|
||||||
7. When the ESP8266 starts up for the first time the onboard LED will be flashing. This is because the EMS bus is not yet connected and receiving data.
|
7. When the ESP8266 starts up for the first time the onboard LED will be flashing. This is because the EMS bus is not yet connected and receiving data.
|
||||||
8. If you haven't hardcoded the WiFi credentials in step 4, the ESP8266 will boot up in a WiFi Access Point (AP) mode with the ssid name `ems-esp`. Now you can either use a laptop and connect to this AP using Telnet to `192.168.1.4` or if its powered from a computers USB use a Serial monitor tool to the ESP's COM port. Tip: to enable Telnet on Windows 10 run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).
|
8. If you haven't hardcoded the WiFi credentials in step 4, the ESP8266 will boot up in a WiFi Access Point (AP) mode with the ssid name `ems-esp`. Now you can either use a laptop and connect to this AP using Telnet to `192.168.1.4` or if its powered from a computers USB use a Serial monitor tool to the ESP's COM port. Tip: to enable Telnet on Windows 10 run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).
|
||||||
9. Next is to customize some of the onboard settings. Type `set` to list the current stored settings and `?` to see the syntax. Use `set wifi` to add your wifi credentials and if you're using MQTT set the host, username and password. There is no need to reboot the ESP.
|
9. Next is to customize some of the onboard settings. Type `set` to list the current stored settings and `?` to see the syntax. Use `set wifi_ssid` and `set wifi_password` to add your WiFi credentials and if you're using MQTT set the host, username and password. There is no need to reboot the ESP.
|
||||||
10. The `led_gpio` will default to the onboard LED (which is probably blinking now). Ignore `thermostat_type` and `boiler_type` as these will be auto-detected hopefully later on.
|
10. The `led_gpio` will default to the onboard LED (which is probably blinking now). Ignore `thermostat_type` and `boiler_type` as these will be auto-detected hopefully later on.
|
||||||
11. **Important**: By default the serial port is enabled and the EMS bus disabled. This is to allow users to configure their ESP via the serial monitor when pluged into a PC/laptop. You must disable serial with `set serial off` to get the EMS transmission working.
|
11. **Important**: By default the serial port is enabled and the EMS bus disabled. This is to allow users to configure their ESP via the serial monitor when pluged into a PC/laptop. You must disable serial with `set serial off` to get the EMS transmission working.
|
||||||
12. Hook up the ESP to the EMS board as follows:
|
12. Hook up the ESP to the EMS board as follows:
|
||||||
@@ -330,7 +330,7 @@ pre-baked firmware for the Wemos D1 mini is available in the GitHub [releases](h
|
|||||||
1. Check if you have **python 2.7** installed. If not [download it](https://www.python.org/downloads/) and make sure you select the option to add Python to the windows PATH
|
1. Check if you have **python 2.7** installed. If not [download it](https://www.python.org/downloads/) and make sure you select the option to add Python to the windows PATH
|
||||||
2. Then install the ESPTool by running `pip install esptool` from a command prompt
|
2. Then install the ESPTool by running `pip install esptool` from a command prompt
|
||||||
|
|
||||||
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the `set wifi` command to configure your own network settings like `set wifi your_ssid your_password`. Alternatively connect the ESP8266 to your PC and open a Serial monitor (with baud 115200) to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
|
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the `set wifi_ssid/set wifi_password` command to configure your own network settings. Alternatively connect the ESP8266 to your PC and open a Serial monitor (with baud 115200) to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
|
||||||
|
|
||||||
`set` wil list all currently stored settings.
|
`set` wil list all currently stored settings.
|
||||||
|
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ void MyESP::_printSetCommands() {
|
|||||||
myDebug_P(PSTR("The following set commands are available:"));
|
myDebug_P(PSTR("The following set commands are available:"));
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR("* set erase"));
|
myDebug_P(PSTR("* set erase"));
|
||||||
myDebug_P(PSTR("* set wifi [ssid] [password]"));
|
myDebug_P(PSTR("* set <wifi_ssid | wifi_password> [value]"));
|
||||||
myDebug_P(PSTR("* set <mqtt_host | mqtt_username | mqtt_password> [value]"));
|
myDebug_P(PSTR("* set <mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||||
myDebug_P(PSTR("* set serial <on | off>"));
|
myDebug_P(PSTR("* set serial <on | off>"));
|
||||||
|
|
||||||
@@ -532,13 +532,15 @@ void MyESP::_printSetCommands() {
|
|||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR("Stored settings:"));
|
myDebug_P(PSTR("Stored settings:"));
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
SerialAndTelnet.printf(PSTR(" wifi=%s "), (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
myDebug_P(PSTR(" wifi_ssid=%s "), (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||||
|
SerialAndTelnet.print(FPSTR(" wifi_password="));
|
||||||
if (!_wifi_password) {
|
if (!_wifi_password) {
|
||||||
SerialAndTelnet.print(FPSTR("<not set>"));
|
SerialAndTelnet.print(FPSTR("<not set>"));
|
||||||
} else {
|
} else {
|
||||||
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
for (uint8_t i = 0; i < strlen(_wifi_password); i++) {
|
||||||
SerialAndTelnet.print(FPSTR("*"));
|
SerialAndTelnet.print(FPSTR("*"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR(" mqtt_host=%s"), (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
myDebug_P(PSTR(" mqtt_host=%s"), (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
||||||
myDebug_P(PSTR(" mqtt_username=%s"), (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
myDebug_P(PSTR(" mqtt_username=%s"), (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
||||||
@@ -546,9 +548,10 @@ void MyESP::_printSetCommands() {
|
|||||||
if (!_mqtt_password) {
|
if (!_mqtt_password) {
|
||||||
SerialAndTelnet.print(FPSTR("<not set>"));
|
SerialAndTelnet.print(FPSTR("<not set>"));
|
||||||
} else {
|
} else {
|
||||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
for (uint8_t i = 0; i < strlen(_mqtt_password); i++) {
|
||||||
SerialAndTelnet.print(FPSTR("*"));
|
SerialAndTelnet.print(FPSTR("*"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR(" serial=%s"), (_use_serial) ? "on" : "off");
|
myDebug_P(PSTR(" serial=%s"), (_use_serial) ? "on" : "off");
|
||||||
@@ -571,54 +574,47 @@ void MyESP::resetESP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read next word from string buffer
|
// read next word from string buffer
|
||||||
char * MyESP::_telnet_readWord() {
|
// if parameter true then a word is only terminated by a newline
|
||||||
return (strtok(NULL, ", \n"));
|
char * MyESP::_telnet_readWord(bool allow_all_chars) {
|
||||||
}
|
if (allow_all_chars) {
|
||||||
|
return (strtok(NULL, "\n")); // allow only newline
|
||||||
// change setting for 2 params (set <command> <value1> <value2>)
|
} else {
|
||||||
void MyESP::_changeSetting2(const char * setting, const char * value1, const char * value2) {
|
return (strtok(NULL, ", \n")); // allow space and comma
|
||||||
if (strcmp(setting, "wifi") == 0) {
|
|
||||||
if (_wifi_ssid)
|
|
||||||
free(_wifi_ssid);
|
|
||||||
if (_wifi_password)
|
|
||||||
free(_wifi_password);
|
|
||||||
_wifi_ssid = NULL;
|
|
||||||
_wifi_password = NULL;
|
|
||||||
|
|
||||||
if (value1) {
|
|
||||||
_wifi_ssid = strdup(value1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value2) {
|
|
||||||
_wifi_password = strdup(value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)fs_saveConfig();
|
|
||||||
myDebug_P(PSTR("WiFi settings changed. Reboot ESP."));
|
|
||||||
//jw.disconnect();
|
|
||||||
//jw.cleanNetworks();
|
|
||||||
//jw.addNetwork(_wifi_ssid, _wifi_password);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// change settings - always as strings
|
// change settings - always as strings
|
||||||
// messy code but effective since we don't have too many settings
|
// messy code but effective since we don't have too many settings
|
||||||
// wc is word count, number of parameters after the 'set' command
|
// wc is word count, number of parameters after the 'set' command
|
||||||
void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) {
|
bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
// check for our internal commands first
|
// check for our internal commands first
|
||||||
if (strcmp(setting, "erase") == 0) {
|
if (strcmp(setting, "erase") == 0) {
|
||||||
_fs_eraseConfig();
|
_fs_eraseConfig();
|
||||||
return;
|
return true;
|
||||||
} else if ((strcmp(setting, "wifi") == 0) && (wc == 1)) { // erase wifi settings
|
|
||||||
|
} else if (strcmp(setting, "wifi_ssid") == 0) {
|
||||||
if (_wifi_ssid)
|
if (_wifi_ssid)
|
||||||
free(_wifi_ssid);
|
free(_wifi_ssid);
|
||||||
|
_wifi_ssid = NULL; // just to be sure
|
||||||
|
if (value) {
|
||||||
|
_wifi_ssid = strdup(value);
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
jw.enableSTA(false);
|
||||||
|
myDebug_P(PSTR("Note: please reboot to apply new WiFi settings"));
|
||||||
|
} else if (strcmp(setting, "wifi_password") == 0) {
|
||||||
if (_wifi_password)
|
if (_wifi_password)
|
||||||
free(_wifi_password);
|
free(_wifi_password);
|
||||||
_wifi_ssid = NULL;
|
_wifi_password = NULL; // just to be sure
|
||||||
_wifi_password = NULL;
|
if (value) {
|
||||||
|
_wifi_password = strdup(value);
|
||||||
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
|
jw.enableSTA(false);
|
||||||
|
myDebug_P(PSTR("Note: please reboot to apply new WiFi settings"));
|
||||||
|
|
||||||
} else if (strcmp(setting, "mqtt_host") == 0) {
|
} else if (strcmp(setting, "mqtt_host") == 0) {
|
||||||
if (_mqtt_host)
|
if (_mqtt_host)
|
||||||
free(_mqtt_host);
|
free(_mqtt_host);
|
||||||
@@ -643,6 +639,7 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
_mqtt_password = strdup(value);
|
_mqtt_password = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
|
|
||||||
} else if (strcmp(setting, "serial") == 0) {
|
} else if (strcmp(setting, "serial") == 0) {
|
||||||
ok = true;
|
ok = true;
|
||||||
_use_serial = false;
|
_use_serial = false;
|
||||||
@@ -664,11 +661,8 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
// if we were able to recognize the set command, continue
|
||||||
myDebug_P(PSTR("\nInvalid parameter for set command."));
|
if (ok) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for 2 params
|
// check for 2 params
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
myDebug_P(PSTR("%s setting reset to its default value."), setting);
|
myDebug_P(PSTR("%s setting reset to its default value."), setting);
|
||||||
@@ -680,12 +674,16 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
|
|
||||||
(void)fs_saveConfig(); // always save the values
|
(void)fs_saveConfig(); // always save the values
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyESP::_telnetCommand(char * commandLine) {
|
void MyESP::_telnetCommand(char * commandLine) {
|
||||||
// count the number of arguments
|
|
||||||
char * str = commandLine;
|
char * str = commandLine;
|
||||||
bool state = false;
|
bool state = false;
|
||||||
|
|
||||||
|
// count the number of arguments
|
||||||
unsigned wc = 0;
|
unsigned wc = 0;
|
||||||
while (*str) {
|
while (*str) {
|
||||||
if (*str == ' ' || *str == '\n' || *str == '\t') {
|
if (*str == ' ' || *str == '\n' || *str == '\t') {
|
||||||
@@ -699,25 +697,27 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
|
|
||||||
// check first for reserved commands
|
// check first for reserved commands
|
||||||
char * temp = strdup(commandLine); // because strotok kills original string buffer
|
char * temp = strdup(commandLine); // because strotok kills original string buffer
|
||||||
char * ptrToCommandName = strtok((char *)temp, ", \n");
|
char * ptrToCommandName = strtok((char *)temp, " \n"); // space and newline
|
||||||
|
|
||||||
// set command
|
// set command
|
||||||
if (strcmp(ptrToCommandName, "set") == 0) {
|
if (strcmp(ptrToCommandName, "set") == 0) {
|
||||||
|
bool ok = false;
|
||||||
if (wc == 1) {
|
if (wc == 1) {
|
||||||
_printSetCommands();
|
_printSetCommands();
|
||||||
} else if (wc == 2) {
|
ok = true;
|
||||||
char * setting = _telnet_readWord();
|
} else if (wc == 2) { // set <xxx>
|
||||||
_changeSetting(1, setting, NULL);
|
char * setting = _telnet_readWord(false);
|
||||||
} else if (wc == 3) {
|
ok = _changeSetting(wc - 1, setting, NULL);
|
||||||
char * setting = _telnet_readWord();
|
} else { // set <xxx> <yyy>
|
||||||
char * value = _telnet_readWord();
|
char * setting = _telnet_readWord(false);
|
||||||
_changeSetting(2, setting, value);
|
char * value = _telnet_readWord(true); // allow strange characters
|
||||||
} else if (wc == 4) {
|
ok = _changeSetting(wc - 1, setting, value);
|
||||||
char * setting = _telnet_readWord();
|
|
||||||
char * value1 = _telnet_readWord();
|
|
||||||
char * value2 = _telnet_readWord();
|
|
||||||
_changeSetting2(setting, value1, value2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
myDebug_P(PSTR("\nInvalid parameter for set command."));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,13 +735,13 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
// crash command
|
// crash command
|
||||||
#ifdef CRASH
|
#ifdef CRASH
|
||||||
if ((strcmp(ptrToCommandName, "crash") == 0) && (wc >= 2)) {
|
if ((strcmp(ptrToCommandName, "crash") == 0) && (wc >= 2)) {
|
||||||
char * cmd = _telnet_readWord();
|
char * cmd = _telnet_readWord(false);
|
||||||
if (strcmp(cmd, "dump") == 0) {
|
if (strcmp(cmd, "dump") == 0) {
|
||||||
crashDump();
|
crashDump();
|
||||||
} else if (strcmp(cmd, "clear") == 0) {
|
} else if (strcmp(cmd, "clear") == 0) {
|
||||||
crashClear();
|
crashClear();
|
||||||
} else if ((strcmp(cmd, "test") == 0) && (wc == 3)) {
|
} else if ((strcmp(cmd, "test") == 0) && (wc == 3)) {
|
||||||
char * value = _telnet_readWord();
|
char * value = _telnet_readWord(false);
|
||||||
crashTest(atoi(value));
|
crashTest(atoi(value));
|
||||||
}
|
}
|
||||||
return; // don't call custom command line callback
|
return; // don't call custom command line callback
|
||||||
@@ -1253,7 +1253,7 @@ void MyESP::_fs_setup() {
|
|||||||
|
|
||||||
// load the config file. if it doesn't exist (function returns false) create it
|
// load the config file. if it doesn't exist (function returns false) create it
|
||||||
if (!_fs_loadConfig()) {
|
if (!_fs_loadConfig()) {
|
||||||
myDebug_P(PSTR("[FS] Re-creating config file"));
|
//myDebug_P(PSTR("[FS] Re-creating config file"));
|
||||||
fs_saveConfig();
|
fs_saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#ifndef MyEMS_h
|
#ifndef MyEMS_h
|
||||||
#define MyEMS_h
|
#define MyEMS_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.1.6b2"
|
#define MYESP_VERSION "1.1.6b3"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
@@ -242,7 +242,7 @@ class MyESP {
|
|||||||
void _telnetDisconnected();
|
void _telnetDisconnected();
|
||||||
void _telnetHandle();
|
void _telnetHandle();
|
||||||
void _telnetCommand(char * commandLine);
|
void _telnetCommand(char * commandLine);
|
||||||
char * _telnet_readWord();
|
char * _telnet_readWord(bool allow_all_chars);
|
||||||
void _telnet_setup();
|
void _telnet_setup();
|
||||||
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
|
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
|
||||||
command_t * _helpProjectCmds; // Help of commands setted by project
|
command_t * _helpProjectCmds; // Help of commands setted by project
|
||||||
@@ -250,8 +250,7 @@ class MyESP {
|
|||||||
void _consoleShowHelp();
|
void _consoleShowHelp();
|
||||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
||||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
||||||
void _changeSetting(uint8_t wc, const char * setting, const char * value);
|
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
|
||||||
void _changeSetting2(const char * setting, const char * value1, const char * value2);
|
|
||||||
|
|
||||||
// fs
|
// fs
|
||||||
void _fs_setup();
|
void _fs_setup();
|
||||||
|
|||||||
Reference in New Issue
Block a user