Skip to content

Commit

Permalink
First WorkInProgress version
Browse files Browse the repository at this point in the history
  • Loading branch information
fsaris committed Jan 29, 2023
0 parents commit 4e6c411
Show file tree
Hide file tree
Showing 9 changed files with 1,159 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
/secrets.yaml
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# EspHome component: AwoX BLE mesh hub
# EspHome component: AwoX BLE mesh hub
39 changes: 39 additions & 0 deletions awox-ble-mesh-hub.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
esphome:
name: "awox-ble-mesh-hub"

esp32:
board: esp32dev
framework:
type: arduino

status_led:
pin: GPIO2

# Enable logging
logger:
#level: VERY_VERBOSE

mqtt:
broker: !secret mqtt_host
username: !secret mqtt_user
password: !secret mqtt_password

ota:
password: "988af1bdbc81aa0760c360430673b560"

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password

external_components:
- source:
type: local
path: components

esp32_ble_tracker:
scan_parameters:
active: false

awox_mesh:
mesh_name: !secret mesh_name
mesh_password: !secret mesh_password
1 change: 1 addition & 0 deletions components/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
49 changes: 49 additions & 0 deletions components/awox_mesh/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker, esp32_ble_client

from esphome.const import CONF_ID

AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
DEPENDENCIES = ["mqtt", "esp32"]

awox_ns = cg.esphome_ns.namespace("awox_mesh")

Awox = awox_ns.class_("AwoxMesh", esp32_ble_tracker.ESPBTDeviceListener, cg.Component)
MeshDevice = awox_ns.class_("MeshDevice", esp32_ble_client.BLEClientBase)

CONNECTION_SCHEMA = esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MeshDevice),
}
).extend(cv.COMPONENT_SCHEMA)

CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Awox),
cv.Required("mesh_name"): cv.string_strict,
cv.Required("mesh_password"): cv.string_strict,
cv.Optional("connection", {}): CONNECTION_SCHEMA,
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)


async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)

connection_var = cg.new_Pvariable(config["connection"][CONF_ID])
cg.add(connection_var.set_mesh_name(config["mesh_name"]))
cg.add(connection_var.set_mesh_password(config["mesh_password"]))

await cg.register_component(connection_var, config["connection"])
cg.add(var.register_connection(connection_var))
await esp32_ble_tracker.register_client(connection_var, config["connection"])

# Crypto
cg.add_library("rweather/Crypto", "0.4.0")
94 changes: 94 additions & 0 deletions components/awox_mesh/awox_mesh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#pragma once

#ifdef USE_ESP32
#include <cstring>
#include <cstdio>
#include <algorithm>
#include "awox_mesh.h"

#include "esphome/core/log.h"

namespace esphome {
namespace awox_mesh {

static const char *const TAG = "AwoxMesh";

FoundDevice AwoxMesh::add_to_devices(const esp32_ble_tracker::ESPBTDevice &device) {
this->devices_.erase(
std::remove_if(this->devices_.begin(), this->devices_.end(),
[device](const FoundDevice &_f) { return _f.address == device.address_uint64(); }),
this->devices_.end());

static FoundDevice found = {};
found.address_str = device.address_str();
found.address = device.address_uint64();
found.rssi = device.get_rssi();
found.last_detected = esphome::millis();
this->devices_.push_back(found);

this->remove_devices_that_are_not_available();

this->sort_devices();

return found;
}

bool AwoxMesh::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (device.address_str().rfind("A4:C1", 0) != 0) {
return false;
}

FoundDevice found = add_to_devices(device);

ESP_LOGV(TAG, "Found Awox device %s - %s. RSSI: %d dB (total devices: %d)", device.get_name().c_str(),
device.address_str().c_str(), device.get_rssi(), this->devices_.size());

return true;
}

void AwoxMesh::setup() {
Component::setup();

this->connection->set_disconnect_callback([this]() { ESP_LOGI(TAG, "disconnected"); });
}

void AwoxMesh::loop() {
if (esphome::millis() - this->start > 20000 && this->devices_.size() > 0 && this->connection->address_str() == "") {
ESP_LOGD(TAG, "Total devices: %d", this->devices_.size());
for (int i = 0; i < this->devices_.size(); i++) {
ESP_LOGD(TAG, "AVailable device %s => rssi: %d", this->devices_[i].address_str.c_str(), this->devices_[i].rssi);
}
auto device = this->devices_.front();

ESP_LOGI(TAG, "Try to connect %s => rssi: %d", device.address_str.c_str(), device.rssi);
this->connection->set_address(device.address);
this->connection->connect();

this->set_timeout("connecting", 20000, [this, device]() {
if (this->connection->connected()) {
return;
}
ESP_LOGI(TAG, "Failed to connect %s => rssi: %d", device.address_str.c_str(), device.rssi);
this->remove_devices_that_are_not_available();
this->connection->disconnect();
this->connection->set_address(0);
});
}
}

void AwoxMesh::sort_devices() {
std::stable_sort(this->devices_.begin(), this->devices_.end(),
[](FoundDevice a, FoundDevice b) { return a.rssi > b.rssi; });
}

void AwoxMesh::remove_devices_that_are_not_available() {
const uint32_t now = esphome::millis();
this->devices_.erase(std::remove_if(this->devices_.begin(), this->devices_.end(),
[&](const FoundDevice &_f) { return now - _f.last_detected > 20000; }),
this->devices_.end());
}

} // namespace awox_mesh
} // namespace esphome

#endif
61 changes: 61 additions & 0 deletions components/awox_mesh/awox_mesh.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#pragma once

#ifdef USE_ESP32

#include <map>
#include <vector>

#include <AES.h>
#include <Crypto.h>
#include <GCM.h>

#include "esphome/core/hal.h"
#include "esphome/components/esp32_ble_client/ble_client_base.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"

#include "mesh_device.h"

namespace esphome {
namespace awox_mesh {

using namespace esp32_ble_client;

struct FoundDevice {
std::string address_str;
uint64_t address{0};
int rssi{0};
uint32_t last_detected;
};

class AwoxMesh : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
uint32_t start;
FoundDevice add_to_devices(const esp32_ble_tracker::ESPBTDevice &device);
void sort_devices();
void remove_devices_that_are_not_available();

public:
void setup() override;

AwoxMesh() { this->start = esphome::millis(); }
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;

void on_scan_end() override { ESP_LOGD("AwoxMesh", "scan end"); }

void register_connection(MeshDevice *connection) {
ESP_LOGD("AwoxMesh", "register_connection");
this->connection = connection;
}
void loop() override;

protected:
MeshDevice *connection;
std::vector<FoundDevice> devices_{};
};

} // namespace awox_mesh
} // namespace esphome

#endif
Loading

0 comments on commit 4e6c411

Please sign in to comment.