Skip to content

Commit

Permalink
allow disabling maintenance service
Browse files Browse the repository at this point in the history
  • Loading branch information
wifwucite committed Jan 23, 2022
1 parent f03e791 commit 98ea062
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 47 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ esp32_ble_controller:
# This automation is not available for the "none" mode, optional for the "bond" mode, and required for the "secure" mode.
security_mode: secure
# allows to disable the maintenance service, default is 'true'
# When 'false', the maintenance service is not exposed, which provides at least some protection when security mode is "none".
# Note: Writeable characteristics like those for switches or fans may still be written by basically anyone.
maintenance: true
# automation that is invoked when the pass key should be displayed, the pass key is available in the automation as "pass_key" variable of type std::string (not available if security mode is "none")
# the example below just logs the pass keys
on_show_pass_key:
Expand Down Expand Up @@ -104,16 +109,18 @@ Note: On some computers (like the MacBook Pro for example) the very first bondin

### Maintenance service

The maintenance BLE service is provided implicitly when you include `esp32_ble_controller` in your yaml configuration. It provides two characteristics:
The maintenance BLE service is provided implicitly when you include `esp32_ble_controller` in your yaml configuration unless you disable it explicitly via the `maintenance` property. It provides two characteristics:

* Command channel (UTF-8 string, read-write):
Allows to send commands to the ESP32 and receives answers back from it. A command is a string which consists of the name of the command and (possibly) arguments, separated by spaces.
You can define your own custom commands in yaml as described below in detail.
There are also some built-in commands, which are always available:
* help [<command>]:
Without argument, it lists all available commands. When the name of a command is given like in "help log-level" it displays a specific description for this command.
* ble-maintenance [off]:
Switches the maintenance service off and boots the device. After the boot the maintenance service will **not be availble anymore until you flash your device again**. Thus you can set up your device with the maintenance service enabled and disable that service as soon as everything is running (if you are operating your device in an insecure mode).
* ble-services [on|off]:
Switches the component related (non-maintenance) BLE services on or off. You may wonder why one should switch off these services. On most ESP32 boards both BLE and WiFi share the same physical 2,4 GHz antenna on the ESP32. So, too much traffic on both of them can cause it to crash and reboot. Short-lived WiFi connections for sending MQTT messages work fine with services enabled. However, when connecting to the [web server](https://esphome.io/components/web_server.html) or for [OTA updates](https://esphome.io/components/ota.html) services should be disabled. (Note that ESPHome permits configurations without the WiFi component, so if you encounter problems with BLE you could try disabling WiFi completely.)
Switches the component related (non-maintenance) BLE services on or off and boots the device. You may wonder why one should switch off these services. On most ESP32 boards both BLE and WiFi share the same physical 2,4 GHz antenna on the ESP32. So, too much traffic on both of them can cause it to crash and reboot. Short-lived WiFi connections for sending MQTT messages work fine with services enabled. However, when connecting to the [web server](https://esphome.io/components/web_server.html) or for [OTA updates](https://esphome.io/components/ota.html) services should be disabled. (Note that ESPHome permits configurations without the WiFi component, so if you encounter problems with BLE you could try disabling WiFi completely.)
* wifi-config <ssid> <password> [hidden]:
Sets the SSID and the password to use for connecting to WiFi. The optional 'hidden' argument marks the network as hidden network. It is recommended to use this command only when security is enabled. You can also use "wifi-config clear" to clear the WiFi configuration; then the default credentials (compiled into the firmware) will be used. (This command is only available if the WiFi component has been configured at all.)
* parings [clear]:
Expand Down
9 changes: 8 additions & 1 deletion components/esp32_ble_controller/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

### Configuration validation ############################################################################################

# BLE services and characteristics #####
# BLE component services and characteristics #####
CONF_BLE_SERVICES = "services"
CONF_BLE_SERVICE = "service"
CONF_BLE_CHARACTERISTICS = "characteristics"
Expand Down Expand Up @@ -72,6 +72,9 @@ def validate_command_id(value):
}),
})

# BLE maintenance services #####
CONF_EXPOSE_MAINTENANCE_SERVICE = "maintenance"

# security mode enumeration #####
CONF_SECURITY_MODE = 'security_mode'
BLESecurityMode = esp32_ble_controller_ns.enum("BLESecurityMode", is_class = True)
Expand Down Expand Up @@ -126,6 +129,8 @@ def required_automations_present(config):

cv.Optional(CONF_BLE_COMMANDS): cv.ensure_list(BLE_COMMAND),

cv.Optional(CONF_EXPOSE_MAINTENANCE_SERVICE, default=True): cv.boolean,

cv.Optional(CONF_SECURITY_MODE, default=CONF_SECURITY_MODE_SECURE): cv.enum(SECURTY_MODE_OPTIONS),

cv.Optional(CONF_ON_SHOW_PASS_KEY): automation.validate_automation({
Expand Down Expand Up @@ -185,6 +190,8 @@ def to_code(config):
for cmd in config.get(CONF_BLE_COMMANDS, []):
yield to_code_command(var, cmd)

cg.add(var.set_maintenance_service_exposed_after_flash(config[CONF_EXPOSE_MAINTENANCE_SERVICE]))

security_enabled = SECURTY_MODE_OPTIONS[config[CONF_SECURITY_MODE]]
cg.add(var.set_security_mode(config[CONF_SECURITY_MODE]))

Expand Down
26 changes: 17 additions & 9 deletions components/esp32_ble_controller/ble_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,29 @@ void BLECommandHelp::execute(const vector<string>& arguments) const {
}
}

// ble-maintenance ///////////////////////////////////////////////////////////////////////////////////////////////

BLECommandSwitchMaintenanceOnOrOff::BLECommandSwitchMaintenanceOnOrOff() : BLECommand("ble-maintenance", "'ble-maintenance off' disables the maintenance BLE service.") {}

void BLECommandSwitchMaintenanceOnOrOff::execute(const vector<string>& arguments) const {
if (!arguments.empty()) {
const string& on_or_off = arguments[0];
global_ble_controller->switch_maintenance_service_exposed(on_or_off != "off");
}
string enabled_or_disabled = global_ble_controller->get_component_services_exposed() ? "disabled" : "enabled";
set_result("Maintenance services is " + enabled_or_disabled +".");
}

// ble-services ///////////////////////////////////////////////////////////////////////////////////////////////

BLECommandSwitchServicesOnOrOff::BLECommandSwitchServicesOnOrOff() : BLECommand("ble-services", "'ble-services on|off' enables or disables the non-maintenance BLE services.") {}
BLECommandSwitchComponentServicesOnOrOff::BLECommandSwitchComponentServicesOnOrOff() : BLECommand("ble-services", "'ble-services on|off' enables or disables the non-maintenance BLE services.") {}

void BLECommandSwitchServicesOnOrOff::execute(const vector<string>& arguments) const {
void BLECommandSwitchComponentServicesOnOrOff::execute(const vector<string>& arguments) const {
if (!arguments.empty()) {
const string& on_or_off = arguments[0];
if (on_or_off == "off") {
global_ble_controller->set_ble_mode(BLEMaintenanceMode::WIFI_ONLY);
} else {
global_ble_controller->set_ble_mode(BLEMaintenanceMode::MIXED);
}
global_ble_controller->switch_component_services_exposed(on_or_off != "off");
}
BLEMaintenanceMode mode = global_ble_controller->get_ble_mode();
string enabled_or_disabled = mode == BLEMaintenanceMode::WIFI_ONLY ? "disabled" : "enabled";
string enabled_or_disabled = global_ble_controller->get_component_services_exposed() ? "disabled" : "enabled";
set_result("Non-maintenance services are " + enabled_or_disabled +".");
}

Expand Down
16 changes: 13 additions & 3 deletions components/esp32_ble_controller/ble_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,22 @@ class BLECommandHelp : public BLECommand {
virtual void execute(const vector<string>& arguments) const override;
};

// ble-maintenance ///////////////////////////////////////////////////////////////////////////////////////////////

class BLECommandSwitchMaintenanceOnOrOff : public BLECommand {
public:
BLECommandSwitchMaintenanceOnOrOff();
virtual ~BLECommandSwitchMaintenanceOnOrOff() {}

virtual void execute(const vector<string>& arguments) const override;
};

// ble-services ///////////////////////////////////////////////////////////////////////////////////////////////

class BLECommandSwitchServicesOnOrOff : public BLECommand {
class BLECommandSwitchComponentServicesOnOrOff : public BLECommand {
public:
BLECommandSwitchServicesOnOrOff();
virtual ~BLECommandSwitchServicesOnOrOff() {}
BLECommandSwitchComponentServicesOnOrOff();
virtual ~BLECommandSwitchComponentServicesOnOrOff() {}

virtual void execute(const vector<string>& arguments) const override;
};
Expand Down
22 changes: 14 additions & 8 deletions components/esp32_ble_controller/ble_maintenance_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ namespace esp32_ble_controller {

static const char *TAG = "ble_maintenance_handler";

BLEMaintenanceHandler::BLEMaintenanceHandler() {
BLEMaintenanceHandler::BLEMaintenanceHandler() : ble_command_characteristic(nullptr) {
commands.push_back(new BLECommandHelp());
commands.push_back(new BLECommandSwitchServicesOnOrOff());
commands.push_back(new BLECommandSwitchMaintenanceOnOrOff());
commands.push_back(new BLECommandSwitchComponentServicesOnOrOff());
#ifdef USE_WIFI
commands.push_back(new BLECommandWifiConfiguration());
#endif
commands.push_back(new BLECommandPairings());
commands.push_back(new BLECommandVersion());

#ifdef USE_LOGGER
log_level = ESPHOME_LOG_LEVEL;
logging_characteristic = nullptr;

commands.push_back(new BLECommandLogLevel());
#endif
}
Expand All @@ -52,8 +56,7 @@ void BLEMaintenanceHandler::setup(BLEServer* ble_server) {
service->start();

#ifdef USE_LOGGER
log_level = ESPHOME_LOG_LEVEL;
if (global_ble_controller->get_ble_mode() != BLEMaintenanceMode::BLE_ONLY) {
if (!global_ble_controller->get_component_services_exposed()) {
log_level = ESPHOME_LOG_LEVEL_CONFIG;
}

Expand Down Expand Up @@ -94,9 +97,12 @@ void BLEMaintenanceHandler::on_command_written() {
}

void BLEMaintenanceHandler::send_command_result(const string& result_message) {
global_ble_controller->execute_in_loop([this, result_message] {
ble_command_characteristic->setValue(result_message);
});
if (ble_command_characteristic != nullptr) {
global_ble_controller->execute_in_loop([this, result_message] {
ble_command_characteristic->setValue(result_message);
});
}

// global_ble_controller->execute_in_loop([this, result_message] {
// const uint32_t delay_millis = 50;
// App.scheduler.set_timeout(global_ble_controller, "command_result", delay_millis, [this, result_message]{ ble_command_characteristic->setValue(result_message); });
Expand Down Expand Up @@ -129,7 +135,7 @@ string remove_logger_magic(const string& message) {
}

void BLEMaintenanceHandler::send_log_message(int level, const char *tag, const char *message) {
if (level <= this->log_level) {
if (logging_characteristic != nullptr && level <= this->log_level) {
logging_characteristic->setValue(remove_logger_magic(message));
logging_characteristic->notify();
}
Expand Down
52 changes: 31 additions & 21 deletions components/esp32_ble_controller/esp32_ble_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ void ESP32BLEController::add_on_disconnected_callback(std::function<void()>&& tr
on_disconnected_callbacks.add(std::move(trigger_function));
}

BLEMaintenanceMode set_feature(BLEMaintenanceMode current_mode, BLEMaintenanceMode feature, bool is_set) {
uint8_t new_mode = static_cast<uint8_t>(current_mode) & (~static_cast<uint8_t>(feature));
if (is_set) {
new_mode |= static_cast<uint8_t>(feature);
}
return static_cast<BLEMaintenanceMode>(new_mode);
}

void ESP32BLEController::set_maintenance_service_exposed_after_flash(bool exposed) {
initial_ble_mode_after_flashing = set_feature(initial_ble_mode_after_flashing, BLEMaintenanceMode::MAINTENANCE_SERVICE, exposed);
}

void ESP32BLEController::set_security_enabled(bool enabled) {
set_security_mode(BLESecurityMode::SECURE);
}
Expand Down Expand Up @@ -138,9 +150,11 @@ void ESP32BLEController::setup_ble_server_and_services() {
ble_server = BLEDevice::createServer();
ble_server->setCallbacks(this);

maintenance_handler->setup(ble_server);
if (get_maintenance_service_exposed()) {
maintenance_handler->setup(ble_server);
}

if (get_ble_mode() != BLEMaintenanceMode::WIFI_ONLY) {
if (get_component_services_exposed()) {
setup_ble_services_for_components();
}
}
Expand Down Expand Up @@ -265,36 +279,32 @@ void ESP32BLEController::initialize_ble_mode() {
// Note: We include the compilation time to force a reset after flashing new firmware
ble_mode_preference = global_preferences->make_preference<uint8_t>(fnv1_hash("ble-mode#" + App.get_compilation_time()));

uint8_t mode;
if (!ble_mode_preference.load(&mode)) {
mode = (uint8_t) BLEMaintenanceMode::BLE_ONLY;
if (!ble_mode_preference.load(&ble_mode)) {
ble_mode = initial_ble_mode_after_flashing;
}

ble_mode = static_cast<BLEMaintenanceMode>(mode);

ESP_LOGCONFIG(TAG, "BLE mode: %d", mode);
ESP_LOGCONFIG(TAG, "BLE mode: %d", static_cast<uint8_t>(ble_mode));
}

void ESP32BLEController::set_ble_mode(BLEMaintenanceMode mode) {
set_ble_mode((uint8_t) mode);
}
void ESP32BLEController::switch_ble_mode(BLEMaintenanceMode newMode) {
if (ble_mode != newMode) {
ESP_LOGI(TAG, "Switching BLE mode to %d and rebooting", static_cast<uint8_t>(newMode));

void ESP32BLEController::set_ble_mode(uint8_t newMode) {
if (newMode > (uint8_t) BLEMaintenanceMode::WIFI_ONLY) {
ESP_LOGI(TAG, "Ignoring unsupported BLE mode %d", newMode);
return;
}

ESP_LOGI(TAG, "Updating BLE mode to %d", newMode);
BLEMaintenanceMode newBleMode = static_cast<BLEMaintenanceMode>(newMode);
if (ble_mode != newBleMode) {
ble_mode = newBleMode;
ble_mode = newMode;
ble_mode_preference.save(&ble_mode);

App.safe_reboot();
}
}

void ESP32BLEController::switch_maintenance_service_exposed(bool exposed) {
switch_ble_mode(set_feature(ble_mode, BLEMaintenanceMode::MAINTENANCE_SERVICE, exposed));
}

void ESP32BLEController::switch_component_services_exposed(bool exposed) {
switch_ble_mode(set_feature(ble_mode, BLEMaintenanceMode::COMPONENT_SERVICES, exposed));
}

void ESP32BLEController::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Low Energy Controller:");
ESP_LOGCONFIG(TAG, " BLE device address: %s", BLEDevice::getAddress().toString().c_str());
Expand Down
12 changes: 9 additions & 3 deletions components/esp32_ble_controller/esp32_ble_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ using std::vector;
namespace esphome {
namespace esp32_ble_controller {

enum class BLEMaintenanceMode : uint8_t { BLE_ONLY, MIXED, WIFI_ONLY };
enum class BLEMaintenanceMode : uint8_t { MAINTENANCE_SERVICE = 1, COMPONENT_SERVICES = 2, ALL = 3 };

enum class BLESecurityMode : uint8_t { NONE, SECURE, BOND };

Expand Down Expand Up @@ -56,6 +56,8 @@ class ESP32BLEController : public Component, private BLESecurityCallbacks, priva
void add_on_connected_callback(std::function<void()>&& trigger_function);
void add_on_disconnected_callback(std::function<void()>&& trigger_function);

void set_maintenance_service_exposed_after_flash(bool exposed);

void set_security_mode(BLESecurityMode mode) { security_mode = mode; }
inline BLESecurityMode get_security_mode() const { return security_mode; }

Expand All @@ -76,8 +78,11 @@ class ESP32BLEController : public Component, private BLESecurityCallbacks, priva
void loop() override;

inline BLEMaintenanceMode get_ble_mode() const { return ble_mode; }
void set_ble_mode(BLEMaintenanceMode mode);
void set_ble_mode(uint8_t mode);
bool get_maintenance_service_exposed() const { return static_cast<uint8_t>(ble_mode) & static_cast<uint8_t>(BLEMaintenanceMode::MAINTENANCE_SERVICE); }
bool get_component_services_exposed() const { return static_cast<uint8_t>(ble_mode) & static_cast<uint8_t>(BLEMaintenanceMode::COMPONENT_SERVICES); }
void switch_ble_mode(BLEMaintenanceMode mode);
void switch_maintenance_service_exposed(bool exposed);
void switch_component_services_exposed(bool exposed);

#ifdef USE_LOGGER
int get_log_level() { return maintenance_handler->get_log_level(); }
Expand Down Expand Up @@ -146,6 +151,7 @@ class ESP32BLEController : public Component, private BLESecurityCallbacks, priva
private:
BLEServer* ble_server;

BLEMaintenanceMode initial_ble_mode_after_flashing{BLEMaintenanceMode::ALL};
BLEMaintenanceMode ble_mode;
ESPPreferenceObject ble_mode_preference;

Expand Down

0 comments on commit 98ea062

Please sign in to comment.