Save Somfy rolling codes in ESP32 memory

In addition to MQTT - in case we lose MQTT persistence.
This commit is contained in:
2025-01-30 22:05:34 +01:00
parent 84f6c8fe10
commit 2103a411ad
2 changed files with 32 additions and 17 deletions
+6 -1
View File
@@ -24,6 +24,10 @@ globals:
# Can be retrieved from MQTT server. Take the key with lowest integer value # Can be retrieved from MQTT server. Take the key with lowest integer value
# from /rolling_code/ path. # from /rolling_code/ path.
initial_value: !secret somfy_remote_id_base initial_value: !secret somfy_remote_id_base
- id: somfy_rolling_codes
type: 'int[13]'
initial_value: '{0,0,0,0,0,0,0,0,0,0,0,0,0}'
restore_value: yes
cover: cover:
- platform: custom - platform: custom
@@ -35,7 +39,8 @@ cover:
auto cover = new SomfyRTS( auto cover = new SomfyRTS(
rfPin, rfPin,
id(somfy_remote_id_base) + i, id(somfy_remote_id_base) + i,
id(mqtt_homeassistant)); id(mqtt_homeassistant),
&(id(somfy_rolling_codes)[i]));
blinds.push_back(cover); blinds.push_back(cover);
App.register_component(cover); App.register_component(cover);
} }
+26 -16
View File
@@ -13,24 +13,34 @@ namespace {
class SomfyRTS : public Component, public Cover { class SomfyRTS : public Component, public Cover {
public: public:
SomfyRTS(int rfPin, int remoteId, esphome::mqtt::MQTTClientComponent* mqtt) { SomfyRTS(int rfPin, int remoteId, esphome::mqtt::MQTTClientComponent* mqtt, int *rollingCode) {
this->rfPin = rfPin; this->rfPin = rfPin;
this->remoteId = remoteId; this->remoteId = remoteId;
this->mqtt = mqtt; this->mqtt = mqtt;
this->mqttTopicPrefix = mqtt->get_topic_prefix() + "/rolling_code/"; this->mqttTopicPrefix = mqtt->get_topic_prefix() + "/rolling_code/";
this->rollingCode = rollingCode;
} }
void setup() override { void setup() override {
pinMode(rfPin, OUTPUT); pinMode(rfPin, OUTPUT);
digitalWrite(rfPin, LOW); digitalWrite(rfPin, LOW);
mqtt->subscribe(mqttTopicPrefix + std::to_string(remoteId),
[=](const std::string &topic, const std::string &payload) { if (*rollingCode == 0) {
if (rollingCode == 0) { // Either a new version was flashed, flash was corrupt, or this firmware
ESP_LOGI(TAG, "Received rolling code for remote #%d from MQTT: %s", // was flashed to a different ESP chip. Either way, try to restore from
remoteId, payload.c_str()); // MQTT.
this->rollingCode = atoi(payload.c_str()); mqtt->subscribe(mqttTopicPrefix + std::to_string(remoteId),
} [=](const std::string &topic, const std::string &payload) {
}, /*qos=*/1 /* (at least once) */); if (*rollingCode == 0) {
ESP_LOGI(TAG, "Received rolling code for remote #%d from MQTT: %s",
remoteId, payload.c_str());
*this->rollingCode = atoi(payload.c_str());
}
}, /*qos=*/1 /* (at least once) */);
} else {
ESP_LOGI(TAG, "Restored rolling code for remote #%d from flash: %d",
remoteId, *rollingCode);
}
} }
CoverTraits get_traits() override { CoverTraits get_traits() override {
@@ -56,7 +66,7 @@ class SomfyRTS : public Component, public Cover {
private: private:
int rfPin; int rfPin;
int remoteId; int remoteId;
int rollingCode = 0; int *rollingCode;
esphome::mqtt::MQTTClientComponent* mqtt; esphome::mqtt::MQTTClientComponent* mqtt;
std::string mqttTopicPrefix; std::string mqttTopicPrefix;
@@ -67,7 +77,7 @@ class SomfyRTS : public Component, public Cover {
* introduce an unknown delay into the method execution timeline. * introduce an unknown delay into the method execution timeline.
*/ */
void send(uint8_t command) { void send(uint8_t command) {
if (rollingCode == 0) { if (*rollingCode == 0) {
ESP_LOGE(TAG, "Remote #%d was requested to run command %d, but has not " ESP_LOGE(TAG, "Remote #%d was requested to run command %d, but has not "
"yet received its rolling code. Refusing to proceed, to " "yet received its rolling code. Refusing to proceed, to "
"avoid crippling MQTT rolling code storage", "avoid crippling MQTT rolling code storage",
@@ -76,21 +86,21 @@ class SomfyRTS : public Component, public Cover {
} }
ESP_LOGI(TAG, "Remote #%d sending command %d with rolling code %d", ESP_LOGI(TAG, "Remote #%d sending command %d with rolling code %d",
remoteId, command, rollingCode); remoteId, command, *rollingCode);
uint8_t frame[] = { uint8_t frame[] = {
0xA7, // Encryption key. 0xA7, // Encryption key.
static_cast<uint8_t>(command << 4), // 4 lsb will be checksum. static_cast<uint8_t>(command << 4), // 4 lsb will be checksum.
static_cast<uint8_t>(rollingCode >> 8), static_cast<uint8_t>(*rollingCode >> 8),
static_cast<uint8_t>(rollingCode), static_cast<uint8_t>(*rollingCode),
static_cast<uint8_t>(remoteId >> 16), static_cast<uint8_t>(remoteId >> 16),
static_cast<uint8_t>(remoteId >> 8), static_cast<uint8_t>(remoteId >> 8),
static_cast<uint8_t>(remoteId), static_cast<uint8_t>(remoteId),
}; };
++rollingCode; ++*rollingCode;
// Checksum calculation: a XOR of all the nibbles. // Checksum calculation: a XOR of all the nibbles.
uint8_t checksum = 0; uint8_t checksum = 0;
@@ -155,6 +165,6 @@ class SomfyRTS : public Component, public Cover {
// when the component is called. This method may be synchronous and // when the component is called. This method may be synchronous and
// introduces an unpredictable delay. // introduces an unpredictable delay.
mqtt->publish(mqttTopicPrefix + std::to_string(remoteId), mqtt->publish(mqttTopicPrefix + std::to_string(remoteId),
std::to_string(rollingCode), /*qos=*/1, /*retain=*/true); std::to_string(*rollingCode), /*qos=*/1, /*retain=*/true);
} }
}; };