Convert enums to scoped enums (#28)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Marius Muja 2023-07-03 09:47:00 -07:00 committed by GitHub
parent 64c409cab9
commit 3721bb5465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 254 additions and 382 deletions

View File

@ -12,21 +12,21 @@ namespace ratgdo {
if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_MOTION) {
this->publish_initial_state(false);
this->parent_->subscribe_motion_state([=](MotionState state) {
this->publish_state(state == MotionState::MOTION_STATE_DETECTED);
this->publish_state(state == MotionState::DETECTED);
});
} else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_OBSTRUCTION) {
this->publish_initial_state(false);
this->parent_->subscribe_obstruction_state([=](ObstructionState state) {
this->publish_state(state == ObstructionState::OBSTRUCTION_STATE_OBSTRUCTED);
this->publish_state(state == ObstructionState::OBSTRUCTED);
});
} else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_MOTOR) {
this->parent_->subscribe_motor_state([=](MotorState state) {
this->publish_state(state == MotorState::MOTOR_STATE_ON);
this->publish_state(state == MotorState::ON);
});
} else if (this->binary_sensor_type_ == SensorType::RATGDO_SENSOR_BUTTON) {
this->publish_initial_state(false);
this->parent_->subscribe_button_state([=](ButtonState state) {
this->publish_state(state == ButtonState::BUTTON_STATE_PRESSED);
this->publish_state(state == ButtonState::PRESSED);
});
}
}

View File

@ -24,26 +24,26 @@ namespace ratgdo {
void RATGDOCover::on_door_state(DoorState state, float position)
{
switch (state) {
case DoorState::DOOR_STATE_OPEN:
case DoorState::OPEN:
this->position = COVER_OPEN;
this->current_operation = COVER_OPERATION_IDLE;
break;
case DoorState::DOOR_STATE_CLOSED:
case DoorState::CLOSED:
this->position = COVER_CLOSED;
this->current_operation = COVER_OPERATION_IDLE;
break;
case DoorState::DOOR_STATE_OPENING:
case DoorState::OPENING:
this->current_operation = COVER_OPERATION_OPENING;
this->position = position;
break;
case DoorState::DOOR_STATE_CLOSING:
case DoorState::CLOSING:
this->current_operation = COVER_OPERATION_CLOSING;
this->position = position;
break;
case DoorState::DOOR_STATE_STOPPED:
case DoorState::STOPPED:
this->current_operation = COVER_OPERATION_IDLE;
this->position = position;
case DoorState::DOOR_STATE_UNKNOWN:
case DoorState::UNKNOWN:
default:
this->current_operation = COVER_OPERATION_IDLE;
this->position = position;

57
components/ratgdo/enum.h Normal file
View File

@ -0,0 +1,57 @@
#define PARENS ()
// Rescan macro tokens 256 times
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define FOR_EACH(macro, name, ...) \
__VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, name, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, name, a1, ...) \
macro(name, a1) \
__VA_OPT__(FOR_EACH_AGAIN PARENS(macro, name, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER
#define ENUM_VARIANT0(name, val) name = val,
#define ENUM_VARIANT(name, tuple) ENUM_VARIANT0 tuple
#define TUPLE(x, y) x, y
#define LPAREN (
#define TO_STRING_CASE0(type, name, val) \
case type::name: \
return #name;
#define TO_STRING_CASE(type, tuple) TO_STRING_CASE0 LPAREN type, TUPLE tuple)
#define FROM_INT_CASE0(type, name, val) \
case val: \
return type::name;
#define FROM_INT_CASE(type, tuple) FROM_INT_CASE0 LPAREN type, TUPLE tuple)
#define ENUM(name, type, ...) \
enum class name : type { \
FOR_EACH(ENUM_VARIANT, name, __VA_ARGS__) \
}; \
inline const char* \
name##_to_string(name _e) \
{ \
switch (_e) { \
FOR_EACH(TO_STRING_CASE, name, __VA_ARGS__) \
default: \
return "UNKNOWN"; \
} \
} \
inline name \
to_##name(type _t, name _unknown) \
{ \
switch (_t) { \
FOR_EACH(FROM_INT_CASE, name, __VA_ARGS__) \
default: \
return _unknown; \
} \
}

View File

@ -32,7 +32,7 @@ namespace ratgdo {
void RATGDOLightOutput::set_state(esphome::ratgdo::LightState state)
{
bool is_on = state == LightState::LIGHT_STATE_ON;
bool is_on = state == LightState::ON;
this->light_state_->current_values.set_state(is_on);
this->light_state_->remote_values.set_state(is_on);
this->light_state_->publish_state();

View File

@ -44,6 +44,9 @@ namespace ratgdo {
this->traits.set_min_value(0.0);
this->traits.set_max_value(180.0);
}
if (this->number_type_ == RATGDO_ROLLING_CODE_COUNTER) {
this->traits.set_max_value(0xfffffff);
}
}
void RATGDONumber::control(float value)

View File

@ -73,7 +73,7 @@ namespace ratgdo {
this->input_gdo_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->input_obst_pin_->pin_mode(gpio::FLAG_INPUT);
this->sw_serial.begin(9600, SWSERIAL_8N1, this->input_gdo_pin_->get_pin(), this->output_gdo_pin_->get_pin(), true);
this->sw_serial_.begin(9600, SWSERIAL_8N1, this->input_gdo_pin_->get_pin(), this->output_gdo_pin_->get_pin(), true);
this->input_obst_pin_->attach_interrupt(RATGDOStore::isr_obstruction, &this->isr_store_, gpio::INTERRUPT_ANY_EDGE);
@ -85,8 +85,8 @@ namespace ratgdo {
void RATGDOComponent::loop()
{
obstruction_loop();
gdo_state_loop();
this->obstruction_loop();
this->gdo_state_loop();
}
void RATGDOComponent::dump_config()
@ -99,58 +99,6 @@ namespace ratgdo {
ESP_LOGCONFIG(TAG, " Remote ID: %d", this->remote_id_);
}
const char* cmd_name(uint16_t cmd)
{
// from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
switch (cmd) {
// sent by opener (motor)
case 0x081:
return "status";
case 0x084:
return "unknown_1";
case 0x085:
return "unknown_2";
case 0x0a1:
return "pair_3_resp";
case 0x284:
return "motor_on";
case 0x393:
return "learn_3_resp";
case 0x401:
return "pair_2_resp";
case 0x48c:
return "openings";
// sent by switch
case 0x080:
return "get_status";
case 0x0a0:
return "pair_3";
case 0x181:
return "learn_2";
case 0x18c:
return "lock";
case 0x280:
return "open";
case 0x281:
return "light";
case 0x285:
return "motion";
case 0x391:
return "learn_1";
case 0x392:
return "learn_3";
case 0x400:
return "pair_2";
case 0x48b:
return "get_openings";
case 0x40a:
return "ttc"; // Time to close
default:
return "unknown";
}
}
uint16_t RATGDOComponent::decode_packet(const WirePacket& packet)
{
uint32_t rolling = 0;
@ -162,9 +110,11 @@ namespace ratgdo {
uint16_t cmd = ((fixed >> 24) & 0xf00) | (data & 0xff);
data &= ~0xf000; // clear parity nibble
if ((fixed & 0xfff) == this->remote_id_) { // my commands
Command cmd_enum = to_Command(cmd, Command::UNKNOWN);
if ((fixed & 0xfffffff) == this->remote_id_) { // my commands
ESP_LOGV(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
return 0;
return static_cast<uint16_t>(Command::UNKNOWN);
} else {
ESP_LOGV(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
}
@ -173,41 +123,41 @@ namespace ratgdo {
uint8_t byte1 = (data >> 16) & 0xff;
uint8_t byte2 = (data >> 24) & 0xff;
ESP_LOGV(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, cmd_name(cmd), byte2, byte1, nibble);
ESP_LOGV(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, Command_to_string(cmd_enum), byte2, byte1, nibble);
if (cmd == command::STATUS) {
if (cmd == Command::STATUS) {
auto door_state = static_cast<DoorState>(nibble);
auto door_state = to_DoorState(nibble, DoorState::UNKNOWN);
auto prev_door_state = *this->door_state;
if (door_state == DoorState::DOOR_STATE_OPENING && prev_door_state == DoorState::DOOR_STATE_CLOSED) {
if (door_state == DoorState::OPENING && prev_door_state == DoorState::CLOSED) {
this->start_opening = millis();
}
if (door_state == DoorState::DOOR_STATE_OPEN && prev_door_state == DoorState::DOOR_STATE_OPENING) {
if (door_state == DoorState::OPEN && prev_door_state == DoorState::OPENING) {
if (this->start_opening > 0) {
auto duration = (millis() - this->start_opening) / 1000;
duration = *this->opening_duration > 0 ? (duration + *this->opening_duration) / 2 : duration;
this->set_opening_duration(round(duration * 10) / 10);
}
}
if (door_state == DoorState::DOOR_STATE_CLOSING && prev_door_state == DoorState::DOOR_STATE_OPEN) {
if (door_state == DoorState::CLOSING && prev_door_state == DoorState::OPEN) {
this->start_closing = millis();
}
if (door_state == DoorState::DOOR_STATE_CLOSED && prev_door_state == DoorState::DOOR_STATE_CLOSING) {
if (door_state == DoorState::CLOSED && prev_door_state == DoorState::CLOSING) {
if (this->start_closing > 0) {
auto duration = (millis() - this->start_closing) / 1000;
duration = *this->closing_duration > 0 ? (duration + *this->closing_duration) / 2 : duration;
this->set_closing_duration(round(duration * 10) / 10);
}
}
if (door_state == DoorState::DOOR_STATE_STOPPED) {
if (door_state == DoorState::STOPPED) {
this->start_opening = -1;
this->start_closing = -1;
}
if (door_state == DoorState::DOOR_STATE_OPEN) {
if (door_state == DoorState::OPEN) {
this->door_position = 1.0;
} else if (door_state == DoorState::DOOR_STATE_CLOSED) {
} else if (door_state == DoorState::CLOSED) {
this->door_position = 0.0;
} else {
if (*this->closing_duration == 0 || *this->opening_duration == 0 || *this->door_position == DOOR_POSITION_UNKNOWN) {
@ -215,78 +165,81 @@ namespace ratgdo {
}
}
if (door_state == DoorState::DOOR_STATE_OPENING && !this->moving_to_position) {
if (door_state == DoorState::OPENING && !this->moving_to_position) {
this->position_sync_while_opening(1.0 - *this->door_position);
this->moving_to_position = true;
}
if (door_state == DoorState::DOOR_STATE_CLOSING && !this->moving_to_position) {
if (door_state == DoorState::CLOSING && !this->moving_to_position) {
this->position_sync_while_closing(*this->door_position);
this->moving_to_position = true;
}
if (door_state == DoorState::DOOR_STATE_OPEN || door_state == DoorState::DOOR_STATE_CLOSED || door_state == DoorState::DOOR_STATE_STOPPED) {
if (door_state == DoorState::OPEN || door_state == DoorState::CLOSED || door_state == DoorState::STOPPED) {
this->cancel_position_sync_callbacks();
}
this->door_state = door_state;
this->light_state = static_cast<LightState>((byte2 >> 1) & 1);
this->lock_state = static_cast<LockState>(byte2 & 1);
this->motion_state = MotionState::MOTION_STATE_CLEAR; // when the status message is read, reset motion state to 0|clear
this->motor_state = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off
this->light_state = static_cast<LightState>((byte2 >> 1) & 1); // safe because it can only be 0 or 1
this->lock_state = static_cast<LockState>(byte2 & 1); // safe because it can only be 0 or 1
this->motion_state = MotionState::CLEAR; // when the status message is read, reset motion state to 0|clear
this->motor_state = MotorState::OFF; // when the status message is read, reset motor state to 0|off
// this->obstruction_state = static_cast<ObstructionState>((byte1 >> 6) & 1);
if (door_state == DoorState::DOOR_STATE_CLOSED && door_state != prev_door_state) {
transmit(command::GET_OPENINGS);
if (door_state == DoorState::CLOSED && door_state != prev_door_state) {
this->transmit(Command::GET_OPENINGS);
}
ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s",
door_state_to_string(*this->door_state),
light_state_to_string(*this->light_state),
lock_state_to_string(*this->lock_state));
} else if (cmd == command::LIGHT) {
DoorState_to_string(*this->door_state),
LightState_to_string(*this->light_state),
LockState_to_string(*this->lock_state));
} else if (cmd == Command::LIGHT) {
if (nibble == 0) {
this->light_state = LightState::LIGHT_STATE_OFF;
this->light_state = LightState::OFF;
} else if (nibble == 1) {
this->light_state = LightState::LIGHT_STATE_ON;
this->light_state = LightState::ON;
} else if (nibble == 2) { // toggle
this->light_state = light_state_toggle(*this->light_state);
}
ESP_LOGD(TAG, "Light: action=%s state=%s",
nibble == 0 ? "OFF" : nibble == 1 ? "ON"
: "TOGGLE",
light_state_to_string(*this->light_state));
} else if (cmd == command::MOTOR_ON) {
this->motor_state = MotorState::MOTOR_STATE_ON;
ESP_LOGD(TAG, "Motor: state=%s", motor_state_to_string(*this->motor_state));
} else if (cmd == command::OPEN) {
this->button_state = (byte1 & 1) == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED;
ESP_LOGD(TAG, "Open: button=%s", button_state_to_string(*this->button_state));
} else if (cmd == command::OPENINGS) {
LightState_to_string(*this->light_state));
} else if (cmd == Command::MOTOR_ON) {
this->motor_state = MotorState::ON;
ESP_LOGD(TAG, "Motor: state=%s", MotorState_to_string(*this->motor_state));
} else if (cmd == Command::OPEN) {
this->button_state = (byte1 & 1) == 1 ? ButtonState::PRESSED : ButtonState::RELEASED;
ESP_LOGD(TAG, "Open: button=%s", ButtonState_to_string(*this->button_state));
} else if (cmd == Command::OPENINGS) {
this->openings = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Openings: %d", *this->openings);
} else if (cmd == command::MOTION) {
this->motion_state = MotionState::MOTION_STATE_DETECTED;
if (*this->light_state == LightState::LIGHT_STATE_OFF) {
transmit(command::GET_STATUS);
} else if (cmd == Command::MOTION) {
this->motion_state = MotionState::DETECTED;
if (*this->light_state == LightState::OFF) {
this->transmit(Command::GET_STATUS);
}
ESP_LOGD(TAG, "Motion: %s", motion_state_to_string(*this->motion_state));
} else {
ESP_LOGV(TAG, "Unhandled command: cmd=%03x nibble=%02x byte1=%02x byte2=%02x fixed=%010" PRIx64 " data=%08" PRIx32, cmd, nibble, byte1, byte2, fixed, data);
ESP_LOGD(TAG, "Motion: %s", MotionState_to_string(*this->motion_state));
} else if (cmd == Command::SET_TTC) {
auto seconds = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Time to close (TTC): %ds", seconds);
}
return cmd;
}
void RATGDOComponent::encode_packet(command::cmd command, uint32_t data, bool increment, WirePacket& packet)
void RATGDOComponent::encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet)
{
uint64_t fixed = ((command & ~0xff) << 24) | this->remote_id_;
uint32_t send_data = (data << 8) | (command & 0xff);
auto cmd = static_cast<uint64_t>(command);
uint64_t fixed = ((cmd & ~0xff) << 24) | this->remote_id_;
uint32_t send_data = (data << 8) | (cmd & 0xff);
ESP_LOGV(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), *this->rolling_code_counter, fixed, send_data);
encode_wireline(*this->rolling_code_counter, fixed, send_data, packet);
print_packet(packet);
this->print_packet(packet);
if (increment) {
increment_rolling_code_counter();
this->increment_rolling_code_counter();
}
}
@ -367,13 +320,13 @@ namespace ratgdo {
// check to see if we got between 3 and 8 low pulses on the line
if (this->isr_store_.obstruction_low_count >= 3 && this->isr_store_.obstruction_low_count <= 8) {
// obstructionCleared();
this->obstruction_state = ObstructionState::OBSTRUCTION_STATE_CLEAR;
this->obstruction_state = ObstructionState::CLEAR;
// if there have been no pulses the line is steady high or low
} else if (this->isr_store_.obstruction_low_count == 0) {
// if the line is high and the last high pulse was more than 70ms ago, then there is an obstruction present
if (this->input_obst_pin_->digital_read() && current_millis - this->isr_store_.last_obstruction_high > 70) {
this->obstruction_state = ObstructionState::OBSTRUCTION_STATE_OBSTRUCTED;
this->obstruction_state = ObstructionState::OBSTRUCTED;
// obstructionDetected();
} else {
// asleep
@ -393,8 +346,8 @@ namespace ratgdo {
static WirePacket rx_packet;
if (!reading_msg) {
while (this->sw_serial.available()) {
uint8_t ser_byte = this->sw_serial.read();
while (this->sw_serial_.available()) {
uint8_t ser_byte = this->sw_serial_.read();
if (ser_byte != 0x55 && ser_byte != 0x01 && ser_byte != 0x00) {
byte_count = 0;
continue;
@ -414,15 +367,15 @@ namespace ratgdo {
}
}
if (reading_msg) {
while (this->sw_serial.available()) {
uint8_t ser_byte = this->sw_serial.read();
while (this->sw_serial_.available()) {
uint8_t ser_byte = this->sw_serial_.read();
rx_packet[byte_count] = ser_byte;
byte_count++;
if (byte_count == PACKET_LENGTH) {
reading_msg = false;
byte_count = 0;
decode_packet(rx_packet);
this->decode_packet(rx_packet);
return;
}
}
@ -431,12 +384,12 @@ namespace ratgdo {
void RATGDOComponent::query_status()
{
transmit(command::GET_STATUS);
transmit(Command::GET_STATUS);
}
void RATGDOComponent::query_openings()
{
transmit(command::GET_OPENINGS);
transmit(Command::GET_OPENINGS);
}
/************************* DOOR COMMUNICATION *************************/
@ -448,20 +401,20 @@ namespace ratgdo {
* The opener requires a specific duration low/high pulse before it will accept
* a message
*/
void RATGDOComponent::transmit(command::cmd command, uint32_t data, bool increment)
void RATGDOComponent::transmit(Command command, uint32_t data, bool increment)
{
WirePacket tx_packet;
encode_packet(command, data, increment, tx_packet);
this->encode_packet(command, data, increment, tx_packet);
this->output_gdo_pin_->digital_write(true); // pull the line high for 1305 micros so the
// door opener responds to the message
delayMicroseconds(1305);
this->output_gdo_pin_->digital_write(false); // bring the line low
delayMicroseconds(1260); // "LOW" pulse duration before the message start
this->sw_serial.write(tx_packet, PACKET_LENGTH);
this->sw_serial_.write(tx_packet, PACKET_LENGTH);
save_rolling_code_counter();
this->save_rolling_code_counter();
}
void RATGDOComponent::sync()
@ -471,15 +424,15 @@ namespace ratgdo {
set_retry(
300, 10, [=](uint8_t r) {
if (*this->door_state != DoorState::DOOR_STATE_UNKNOWN) { // have status
if (*this->door_state != DoorState::UNKNOWN) { // have status
if (*this->openings != 0) { // have openings
return RetryResult::DONE;
} else {
transmit(command::GET_OPENINGS);
this->transmit(Command::GET_OPENINGS);
return RetryResult::RETRY;
}
} else {
transmit(command::GET_STATUS);
this->transmit(Command::GET_STATUS);
return RetryResult::RETRY;
}
},
@ -488,41 +441,41 @@ namespace ratgdo {
void RATGDOComponent::open_door()
{
if (*this->door_state == DoorState::DOOR_STATE_OPENING) {
if (*this->door_state == DoorState::OPENING) {
return; // gets ignored by opener
}
this->cancel_position_sync_callbacks();
door_command(data::DOOR_OPEN);
this->door_command(data::DOOR_OPEN);
}
void RATGDOComponent::close_door()
{
if (*this->door_state == DoorState::DOOR_STATE_CLOSING || *this->door_state == DoorState::DOOR_STATE_OPENING) {
if (*this->door_state == DoorState::CLOSING || *this->door_state == DoorState::OPENING) {
return; // gets ignored by opener
}
this->cancel_position_sync_callbacks();
door_command(data::DOOR_CLOSE);
this->door_command(data::DOOR_CLOSE);
}
void RATGDOComponent::stop_door()
{
if (*this->door_state != DoorState::DOOR_STATE_OPENING && *this->door_state != DoorState::DOOR_STATE_CLOSING) {
if (*this->door_state != DoorState::OPENING && *this->door_state != DoorState::CLOSING) {
ESP_LOGW(TAG, "The door is not moving.");
return;
}
door_command(data::DOOR_STOP);
this->door_command(data::DOOR_STOP);
}
void RATGDOComponent::toggle_door()
{
if (*this->door_state == DoorState::DOOR_STATE_OPENING) {
if (*this->door_state == DoorState::OPENING) {
return; // gets ignored by opener
}
this->cancel_position_sync_callbacks();
door_command(data::DOOR_TOGGLE);
this->door_command(data::DOOR_TOGGLE);
}
void RATGDOComponent::position_sync_while_opening(float delta, float update_period)
@ -563,7 +516,7 @@ namespace ratgdo {
void RATGDOComponent::door_move_to_position(float position)
{
if (*this->door_state == DoorState::DOOR_STATE_OPENING || *this->door_state == DoorState::DOOR_STATE_CLOSING) {
if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) {
ESP_LOGW(TAG, "The door is moving, ignoring.");
return;
}
@ -581,11 +534,11 @@ namespace ratgdo {
}
if (delta > 0) { // open
door_command(data::DOOR_OPEN);
this->door_command(data::DOOR_OPEN);
this->position_sync_while_opening(delta);
} else { // close
delta = -delta;
door_command(data::DOOR_CLOSE);
this->door_command(data::DOOR_CLOSE);
this->position_sync_while_closing(delta);
}
@ -593,7 +546,7 @@ namespace ratgdo {
ESP_LOGD(TAG, "Moving to position %.2f in %.1fs", position, operation_time / 1000.0);
this->moving_to_position = true;
set_timeout("move_to_position", operation_time, [=] {
door_command(data::DOOR_STOP);
this->door_command(data::DOOR_STOP);
this->moving_to_position = false;
this->door_position = position;
});
@ -613,48 +566,48 @@ namespace ratgdo {
{
data |= (1 << 16); // button 1 ?
data |= (1 << 8); // button press
transmit(command::OPEN, data, false);
this->transmit(Command::OPEN, data, false);
set_timeout(100, [=] {
auto data2 = data & ~(1 << 8); // button release
transmit(command::OPEN, data2);
this->transmit(Command::OPEN, data2);
});
}
void RATGDOComponent::light_on()
{
this->light_state = LightState::LIGHT_STATE_ON;
transmit(command::LIGHT, data::LIGHT_ON);
this->light_state = LightState::ON;
this->transmit(Command::LIGHT, data::LIGHT_ON);
}
void RATGDOComponent::light_off()
{
this->light_state = LightState::LIGHT_STATE_OFF;
transmit(command::LIGHT, data::LIGHT_OFF);
this->light_state = LightState::OFF;
this->transmit(Command::LIGHT, data::LIGHT_OFF);
}
void RATGDOComponent::toggle_light()
{
this->light_state = light_state_toggle(*this->light_state);
transmit(command::LIGHT, data::LIGHT_TOGGLE);
this->transmit(Command::LIGHT, data::LIGHT_TOGGLE);
}
// Lock functions
void RATGDOComponent::lock()
{
this->lock_state = LockState::LOCK_STATE_LOCKED;
transmit(command::LOCK, data::LOCK_ON);
this->lock_state = LockState::LOCKED;
this->transmit(Command::LOCK, data::LOCK_ON);
}
void RATGDOComponent::unlock()
{
this->lock_state = LockState::LOCK_STATE_UNLOCKED;
transmit(command::LOCK, data::LOCK_OFF);
this->lock_state = LockState::UNLOCKED;
this->transmit(Command::LOCK, data::LOCK_OFF);
}
void RATGDOComponent::toggle_lock()
{
this->lock_state = lock_state_toggle(*this->lock_state);
transmit(command::LOCK, data::LOCK_TOGGLE);
this->transmit(Command::LOCK, data::LOCK_TOGGLE);
}
void RATGDOComponent::save_rolling_code_counter()

View File

@ -13,6 +13,7 @@
#pragma once
#include "SoftwareSerial.h" // Using espsoftwareserial https://github.com/plerup/espsoftwareserial
#include "enum.h"
#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
#include "esphome/core/log.h"
@ -25,6 +26,7 @@ extern "C" {
#include "ratgdo_state.h"
namespace esphome {
namespace ratgdo {
@ -36,34 +38,6 @@ namespace ratgdo {
const float DOOR_POSITION_UNKNOWN = -1.0;
/*
from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
_WIRELINE_COMMANDS = {
# sent by opener
0x081: "status",
0x084: "unknown_1",
0x085: "unknown_2",
0x0a1: "pair_3_resp",
0x284: "motor_on",
0x393: "learn_3_resp",
0x401: "pair_2_resp",
0x48c: "openings",
# sent by switch
0x080: "get_status",
0x0a0: "pair_3",
0x181: "learn_2",
0x18c: "lock",
0x280: "open",
0x281: "light",
0x285: "motion",
0x391: "learn_1",
0x392: "learn_3",
0x400: "pair_2",
0x48b: "get_openings",
}
*/
namespace data {
const uint32_t LIGHT_OFF = 0;
const uint32_t LIGHT_ON = 1;
@ -80,36 +54,37 @@ namespace ratgdo {
const uint32_t DOOR_STOP = 3;
}
namespace command {
ENUM(Command, uint16_t,
(UNKNOWN, 0x000),
(GET_STATUS, 0x080),
(STATUS, 0x081),
(OBST_1, 0x084), // sent when an obstruction happens?
(OBST_2, 0x085), // sent when an obstruction happens?
(PAIR_3, 0x0a0),
(PAIR_3_RESP, 0x0a1),
enum cmd : uint64_t {
GET_STATUS = 0x080,
STATUS = 0x081,
OBST_1 = 0x084, // sent when an obstruction happens?
OBST_2 = 0x085, // sent when an obstruction happens?
PAIR_3 = 0x0a0,
PAIR_3_RESP = 0x0a1,
(LEARN_2, 0x181),
(LOCK, 0x18c),
(OPEN, 0x280),
(LIGHT, 0x281),
(MOTOR_ON, 0x284),
(MOTION, 0x285),
LEARN_2 = 0x181,
LOCK = 0x18c,
(LEARN_1, 0x391),
(PING, 0x392),
(PING_RESP, 0x393),
OPEN = 0x280,
LIGHT = 0x281,
MOTOR_ON = 0x284,
MOTION = 0x285,
(PAIR_2, 0x400),
(PAIR_2_RESP, 0x401),
(SET_TTC, 0x402), // ttc_in_seconds = (byte1<<8)+byte2
(CANCEL_TTC, 0x408), // ?
(TTC, 0x40a), // Time to close
(GET_OPENINGS, 0x48b),
(OPENINGS, 0x48c), // openings = (byte1<<8)+byte2
)
LEARN_1 = 0x391,
LEARN_3 = 0x392,
LEARN_3_RESP = 0x393,
PAIR_2 = 0x400,
PAIR_2_RESP = 0x401,
TTC = 0x40a, // Time to close
GET_OPENINGS = 0x48b,
OPENINGS = 0x48c,
};
}
inline bool operator==(const uint16_t cmd_i, const Command& cmd_e) { return cmd_i == static_cast<uint16_t>(cmd_e); }
inline bool operator==(const Command& cmd_e, const uint16_t cmd_i) { return cmd_i == static_cast<uint16_t>(cmd_e); }
struct RATGDOStore {
ISRInternalGPIOPin input_obst;
@ -126,8 +101,6 @@ namespace ratgdo {
void loop() override;
void dump_config() override;
EspSoftwareSerial::UART sw_serial;
observable<uint32_t> rolling_code_counter { 0 };
float start_opening { -1 };
@ -137,27 +110,28 @@ namespace ratgdo {
observable<uint16_t> openings { 0 }; // number of times the door has been opened
observable<DoorState> door_state { DoorState::DOOR_STATE_UNKNOWN };
observable<DoorState> door_state { DoorState::UNKNOWN };
observable<float> door_position { DOOR_POSITION_UNKNOWN };
bool moving_to_position { false };
observable<LightState> light_state { LightState::LIGHT_STATE_UNKNOWN };
observable<LockState> lock_state { LockState::LOCK_STATE_UNKNOWN };
observable<ObstructionState> obstruction_state { ObstructionState::OBSTRUCTION_STATE_UNKNOWN };
observable<MotorState> motor_state { MotorState::MOTOR_STATE_UNKNOWN };
observable<ButtonState> button_state { ButtonState::BUTTON_STATE_UNKNOWN };
observable<MotionState> motion_state { MotionState::MOTION_STATE_UNKNOWN };
observable<LightState> light_state { LightState::UNKNOWN };
observable<LockState> lock_state { LockState::UNKNOWN };
observable<ObstructionState> obstruction_state { ObstructionState::UNKNOWN };
observable<MotorState> motor_state { MotorState::UNKNOWN };
observable<ButtonState> button_state { ButtonState::UNKNOWN };
observable<MotionState> motion_state { MotionState::UNKNOWN };
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }
void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; }
void set_input_obst_pin(InternalGPIOPin* pin) { this->input_obst_pin_ = pin; }
void set_remote_id(uint64_t remote_id) { this->remote_id_ = remote_id & 0xffffff; } // not sure how large remote_id can be, assuming not more than 24 bits
uint64_t get_remote_id() { return this->remote_id_; }
void gdo_state_loop();
uint16_t decode_packet(const WirePacket& packet);
void obstruction_loop();
void transmit(command::cmd command, uint32_t data = 0, bool increment = true);
void encode_packet(command::cmd command, uint32_t data, bool increment, WirePacket& packet);
void transmit(Command command, uint32_t data = 0, bool increment = true);
void encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet);
void print_packet(const WirePacket& packet) const;
void increment_rolling_code_counter(int delta = 1);
@ -211,6 +185,7 @@ namespace ratgdo {
ESPPreferenceObject opening_duration_pref_;
ESPPreferenceObject closing_duration_pref_;
RATGDOStore isr_store_ {};
SoftwareSerial sw_serial_;
InternalGPIOPin* output_gdo_pin_;
InternalGPIOPin* input_gdo_pin_;

View File

@ -1,132 +1,33 @@
#include "ratgdo_state.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ratgdo {
const char* door_state_to_string(DoorState state)
{
switch (state) {
case DOOR_STATE_OPEN:
return "OPEN";
case DOOR_STATE_CLOSED:
return "CLOSED";
case DOOR_STATE_STOPPED:
return "STOPPED";
case DOOR_STATE_OPENING:
return "OPENING";
case DOOR_STATE_CLOSING:
return "CLOSING";
case DOOR_STATE_UNKNOWN:
default:
return "UNKNOWN";
}
}
const char* light_state_to_string(LightState state)
{
switch (state) {
case LIGHT_STATE_OFF:
return "OFF";
case LIGHT_STATE_ON:
return "ON";
// 2 and 3 appears sometimes
case LIGHT_STATE_UNKNOWN:
default:
return "UNKNOWN";
}
}
LightState light_state_toggle(LightState state)
{
switch (state) {
case LIGHT_STATE_OFF:
return LIGHT_STATE_ON;
case LIGHT_STATE_ON:
return LIGHT_STATE_OFF;
case LightState::OFF:
return LightState::ON;
case LightState::ON:
return LightState::OFF;
// 2 and 3 appears sometimes
case LIGHT_STATE_UNKNOWN:
case LightState::UNKNOWN:
default:
return LIGHT_STATE_UNKNOWN;
}
}
const char* lock_state_to_string(LockState state)
{
switch (state) {
case LOCK_STATE_UNLOCKED:
return "UNLOCKED";
case LOCK_STATE_LOCKED:
return "LOCKED";
case LOCK_STATE_UNKNOWN:
default:
return "UNKNOWN";
return LightState::UNKNOWN;
}
}
LockState lock_state_toggle(LockState state)
{
switch (state) {
case LOCK_STATE_UNLOCKED:
return LOCK_STATE_LOCKED;
case LOCK_STATE_LOCKED:
return LOCK_STATE_UNLOCKED;
case LockState::UNLOCKED:
return LockState::LOCKED;
case LockState::LOCKED:
return LockState::UNLOCKED;
// 2 and 3 appears sometimes
case LOCK_STATE_UNKNOWN:
case LockState::UNKNOWN:
default:
return LOCK_STATE_UNKNOWN;
}
}
const char* motion_state_to_string(MotionState state)
{
switch (state) {
case MOTION_STATE_CLEAR:
return "CLEAR";
case MOTION_STATE_DETECTED:
return "DETECTED";
case MOTION_STATE_UNKNOWN:
default:
return "UNKNOWN";
}
}
const char* motor_state_to_string(MotorState state)
{
switch (state) {
case MOTOR_STATE_ON:
return "ON";
case MOTOR_STATE_OFF:
return "OFF";
default:
return "UNKNOWN";
}
}
const char* obstruction_state_to_string(ObstructionState state)
{
switch (state) {
case OBSTRUCTION_STATE_CLEAR:
return "CLEAR";
case OBSTRUCTION_STATE_OBSTRUCTED:
return "OBSTRUCTED";
case OBSTRUCTION_STATE_UNKNOWN:
default:
return "UNKNOWN";
}
}
const char* button_state_to_string(ButtonState state)
{
switch (state) {
case BUTTON_STATE_PRESSED:
return "PRESSED";
case BUTTON_STATE_RELEASED:
return "RELEASED";
case BUTTON_STATE_UNKNOWN:
default:
return "UNKNOWN";
return LockState::UNKNOWN;
}
}

View File

@ -12,74 +12,57 @@
************************************/
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
#include "esphome/core/log.h"
#include "esphome/core/preferences.h"
#include "enum.h"
#include <cstdint>
namespace esphome {
namespace ratgdo {
/// Enum for all states a the door can be in.
enum DoorState : uint8_t {
DOOR_STATE_UNKNOWN = 0,
DOOR_STATE_OPEN = 1,
DOOR_STATE_CLOSED = 2,
DOOR_STATE_STOPPED = 3,
DOOR_STATE_OPENING = 4,
DOOR_STATE_CLOSING = 5
};
const char* door_state_to_string(DoorState state);
ENUM(DoorState, uint8_t,
(UNKNOWN, 0),
(OPEN, 1),
(CLOSED, 2),
(STOPPED, 3),
(OPENING, 4),
(CLOSING, 5))
/// Enum for all states a the light can be in.
enum LightState : uint8_t {
LIGHT_STATE_OFF = 0,
LIGHT_STATE_ON = 1,
LIGHT_STATE_UNKNOWN = 2,
};
const char* light_state_to_string(LightState state);
ENUM(LightState, uint8_t,
(OFF, 0),
(ON, 1),
(UNKNOWN, 2))
LightState light_state_toggle(LightState state);
/// Enum for all states a the lock can be in.
enum LockState : uint8_t {
LOCK_STATE_UNLOCKED = 0,
LOCK_STATE_LOCKED = 1,
LOCK_STATE_UNKNOWN = 2,
};
const char* lock_state_to_string(LockState state);
ENUM(LockState, uint8_t,
(UNLOCKED, 0),
(LOCKED, 1),
(UNKNOWN, 2))
LockState lock_state_toggle(LockState state);
/// Enum for all states a the motion can be in.
enum MotionState : uint8_t {
MOTION_STATE_CLEAR = 0,
MOTION_STATE_DETECTED = 1,
MOTION_STATE_UNKNOWN = 2,
};
const char* motion_state_to_string(MotionState state);
/// MotionState for all states a the motion can be in.
ENUM(MotionState, uint8_t,
(CLEAR, 0),
(DETECTED, 1),
(UNKNOWN, 2))
/// Enum for all states a the obstruction can be in.
enum ObstructionState : uint8_t {
OBSTRUCTION_STATE_OBSTRUCTED = 0,
OBSTRUCTION_STATE_CLEAR = 1,
OBSTRUCTION_STATE_UNKNOWN = 2,
};
const char* obstruction_state_to_string(ObstructionState state);
ENUM(ObstructionState, uint8_t,
(OBSTRUCTED, 0),
(CLEAR, 1),
(UNKNOWN, 2))
/// Enum for all states a the motor can be in.
enum MotorState : uint8_t {
MOTOR_STATE_OFF = 0,
MOTOR_STATE_ON = 1,
MOTOR_STATE_UNKNOWN = 2,
};
const char* motor_state_to_string(MotorState state);
ENUM(MotorState, uint8_t,
(OFF, 0),
(ON, 1),
(UNKNOWN, 2))
/// Enum for all states the button can be in.
enum ButtonState : uint8_t {
BUTTON_STATE_PRESSED = 0,
BUTTON_STATE_RELEASED = 1,
BUTTON_STATE_UNKNOWN = 2,
};
const char* button_state_to_string(ButtonState state);
ENUM(ButtonState, uint8_t,
(PRESSED, 0),
(RELEASED, 1),
(UNKNOWN, 2))
} // namespace ratgdo
} // namespace esphome

View File

@ -22,7 +22,7 @@ namespace ratgdo {
void RATGDOSwitch::on_lock_state(LockState state)
{
bool value = state == LockState::LOCK_STATE_LOCKED;
bool value = state == LockState::LOCKED;
this->state = value;
this->publish_state(value);
}