Door position control (#20)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
073b5440cb
commit
f1993931a7
16
base.yaml
16
base.yaml
|
@ -137,6 +137,22 @@ number:
|
||||||
mode: box
|
mode: box
|
||||||
unit_of_measurement: "codes"
|
unit_of_measurement: "codes"
|
||||||
|
|
||||||
|
- platform: ratgdo
|
||||||
|
id: ${id_prefix}_opening_duration
|
||||||
|
type: opening_duration
|
||||||
|
entity_category: config
|
||||||
|
ratgdo_id: ${id_prefix}
|
||||||
|
name: "Opening duration"
|
||||||
|
unit_of_measurement: "s"
|
||||||
|
|
||||||
|
- platform: ratgdo
|
||||||
|
id: ${id_prefix}_closing_duration
|
||||||
|
type: closing_duration
|
||||||
|
entity_category: config
|
||||||
|
ratgdo_id: ${id_prefix}
|
||||||
|
name: "Closing duration"
|
||||||
|
unit_of_measurement: "s"
|
||||||
|
|
||||||
cover:
|
cover:
|
||||||
- platform: ratgdo
|
- platform: ratgdo
|
||||||
id: ${id_prefix}_garage_door
|
id: ${id_prefix}_garage_door
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace ratgdo {
|
||||||
{
|
{
|
||||||
LOG_COVER("", "RATGDO Cover", this);
|
LOG_COVER("", "RATGDO Cover", this);
|
||||||
}
|
}
|
||||||
void RATGDOCover::on_door_state(DoorState state)
|
void RATGDOCover::on_door_state(DoorState state, float position)
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case DoorState::DOOR_STATE_OPEN:
|
case DoorState::DOOR_STATE_OPEN:
|
||||||
|
@ -26,15 +26,15 @@ namespace ratgdo {
|
||||||
break;
|
break;
|
||||||
case DoorState::DOOR_STATE_OPENING:
|
case DoorState::DOOR_STATE_OPENING:
|
||||||
this->current_operation = COVER_OPERATION_OPENING;
|
this->current_operation = COVER_OPERATION_OPENING;
|
||||||
this->position = 0.5;
|
this->position = position;
|
||||||
break;
|
break;
|
||||||
case DoorState::DOOR_STATE_CLOSING:
|
case DoorState::DOOR_STATE_CLOSING:
|
||||||
this->current_operation = COVER_OPERATION_CLOSING;
|
this->current_operation = COVER_OPERATION_CLOSING;
|
||||||
this->position = 0.5;
|
this->position = position;
|
||||||
break;
|
break;
|
||||||
case DoorState::DOOR_STATE_STOPPED:
|
case DoorState::DOOR_STATE_STOPPED:
|
||||||
this->current_operation = COVER_OPERATION_IDLE;
|
this->current_operation = COVER_OPERATION_IDLE;
|
||||||
this->position = 0.5;
|
this->position = position;
|
||||||
default:
|
default:
|
||||||
this->current_operation = COVER_OPERATION_IDLE;
|
this->current_operation = COVER_OPERATION_IDLE;
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ namespace ratgdo {
|
||||||
this->parent_->openDoor();
|
this->parent_->openDoor();
|
||||||
} else if (pos == COVER_CLOSED) {
|
} else if (pos == COVER_CLOSED) {
|
||||||
this->parent_->closeDoor();
|
this->parent_->closeDoor();
|
||||||
|
} else {
|
||||||
|
this->parent_->setDoorPosition(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace ratgdo {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
void on_door_state(DoorState state) override;
|
void on_door_state(DoorState state, float position) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void control(const cover::CoverCall& call) override;
|
void control(const cover::CoverCall& call) override;
|
||||||
|
|
|
@ -13,6 +13,8 @@ NumberType = ratgdo_ns.enum("NumberType")
|
||||||
CONF_TYPE = "type"
|
CONF_TYPE = "type"
|
||||||
TYPES = {
|
TYPES = {
|
||||||
"rolling_code_counter": NumberType.RATGDO_ROLLING_CODE_COUNTER,
|
"rolling_code_counter": NumberType.RATGDO_ROLLING_CODE_COUNTER,
|
||||||
|
"opening_duration": NumberType.RATGDO_OPENING_DURATION,
|
||||||
|
"closing_duration": NumberType.RATGDO_CLOSING_DURATION,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,56 @@ namespace ratgdo {
|
||||||
void RATGDONumber::dump_config()
|
void RATGDONumber::dump_config()
|
||||||
{
|
{
|
||||||
LOG_NUMBER("", "RATGDO Number", this);
|
LOG_NUMBER("", "RATGDO Number", this);
|
||||||
|
if (this->number_type_ == RATGDO_ROLLING_CODE_COUNTER) {
|
||||||
ESP_LOGCONFIG(TAG, " Type: Rolling Code Counter");
|
ESP_LOGCONFIG(TAG, " Type: Rolling Code Counter");
|
||||||
|
} else if (this->number_type_ == RATGDO_OPENING_DURATION) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Type: Opening Duration");
|
||||||
|
} else if (this->number_type_ == RATGDO_CLOSING_DURATION) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Type: Closing Duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDONumber::set_number_type(NumberType number_type_)
|
||||||
|
{
|
||||||
|
this->number_type_ = number_type_;
|
||||||
|
if (this->number_type_ == RATGDO_OPENING_DURATION || this->number_type_ == RATGDO_CLOSING_DURATION) {
|
||||||
|
this->traits.set_step(0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDONumber::on_rolling_code_change(uint32_t rollingCodeCounter)
|
void RATGDONumber::on_rolling_code_change(uint32_t rollingCodeCounter)
|
||||||
{
|
{
|
||||||
|
if (this->number_type_ != RATGDO_ROLLING_CODE_COUNTER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this->publish_state(rollingCodeCounter);
|
this->publish_state(rollingCodeCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RATGDONumber::on_opening_duration_change(float duration)
|
||||||
|
{
|
||||||
|
if (this->number_type_ != RATGDO_OPENING_DURATION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->publish_state(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDONumber::on_closing_duration_change(float duration)
|
||||||
|
{
|
||||||
|
if (this->number_type_ != RATGDO_CLOSING_DURATION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->publish_state(duration);
|
||||||
|
}
|
||||||
|
|
||||||
void RATGDONumber::control(float value)
|
void RATGDONumber::control(float value)
|
||||||
{
|
{
|
||||||
|
if (this->number_type_ == RATGDO_ROLLING_CODE_COUNTER) {
|
||||||
this->parent_->setRollingCodeCounter(value);
|
this->parent_->setRollingCodeCounter(value);
|
||||||
this->publish_state(value);
|
} else if (this->number_type_ == RATGDO_OPENING_DURATION) {
|
||||||
|
this->parent_->setOpeningDuration(value);
|
||||||
|
} else if (this->number_type_ == RATGDO_CLOSING_DURATION) {
|
||||||
|
this->parent_->setClosingDuration(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ratgdo
|
} // namespace ratgdo
|
||||||
|
|
|
@ -10,15 +10,20 @@ namespace esphome {
|
||||||
namespace ratgdo {
|
namespace ratgdo {
|
||||||
|
|
||||||
enum NumberType {
|
enum NumberType {
|
||||||
RATGDO_ROLLING_CODE_COUNTER
|
RATGDO_ROLLING_CODE_COUNTER,
|
||||||
|
RATGDO_OPENING_DURATION,
|
||||||
|
RATGDO_CLOSING_DURATION,
|
||||||
};
|
};
|
||||||
|
|
||||||
class RATGDONumber : public number::Number, public RATGDOClient, public Component {
|
class RATGDONumber : public number::Number, public RATGDOClient, public Component {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_number_type(NumberType number_type_) { this->number_type_ = number_type_; }
|
void set_number_type(NumberType number_type_);
|
||||||
|
|
||||||
void on_rolling_code_change(uint32_t rollingCodeCounter) override;
|
void on_rolling_code_change(uint32_t rollingCodeCounter) override;
|
||||||
|
void on_opening_duration_change(float duration) override;
|
||||||
|
void on_closing_duration_change(float duration) override;
|
||||||
|
|
||||||
void control(float value) override;
|
void control(float value) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -46,10 +46,23 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::setup()
|
void RATGDOComponent::setup()
|
||||||
{
|
{
|
||||||
this->pref_ = global_preferences->make_preference<int>(734874333U);
|
this->rollingCodePref_ = global_preferences->make_preference<int>(734874333U);
|
||||||
if (!this->pref_.load(&this->rollingCodeCounter)) {
|
if (!this->rollingCodePref_.load(&this->rollingCodeCounter)) {
|
||||||
this->rollingCodeCounter = 0;
|
this->rollingCodeCounter = 0;
|
||||||
}
|
}
|
||||||
|
this->openingDurationPref_ = global_preferences->make_preference<float>(734874334U);
|
||||||
|
if (!this->openingDurationPref_.load(&this->openingDuration)) {
|
||||||
|
this->setOpeningDuration(0);
|
||||||
|
} else {
|
||||||
|
this->sendOpeningDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->closingDurationPref_ = global_preferences->make_preference<float>(734874335U);
|
||||||
|
if (!this->closingDurationPref_.load(&this->closingDuration)) {
|
||||||
|
this->setClosingDuration(0.f);
|
||||||
|
} else {
|
||||||
|
this->sendClosingDuration();
|
||||||
|
}
|
||||||
|
|
||||||
this->output_gdo_pin_->setup();
|
this->output_gdo_pin_->setup();
|
||||||
this->input_gdo_pin_->setup();
|
this->input_gdo_pin_->setup();
|
||||||
|
@ -157,31 +170,81 @@ namespace ratgdo {
|
||||||
data &= ~0xf000; // clear parity nibble
|
data &= ~0xf000; // clear parity nibble
|
||||||
|
|
||||||
if ((fixed & 0xfff) == this->remote_id) { // my commands
|
if ((fixed & 0xfff) == this->remote_id) { // my commands
|
||||||
ESP_LOGV(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
ESP_LOGD(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
ESP_LOGD(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
nibble = (data >> 8) & 0xff;
|
nibble = (data >> 8) & 0xff;
|
||||||
byte1 = (data >> 16) & 0xff;
|
byte1 = (data >> 16) & 0xff;
|
||||||
byte2 = (data >> 24) & 0xff;
|
byte2 = (data >> 24) & 0xff;
|
||||||
|
|
||||||
ESP_LOGV(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, cmd_name(cmd), byte2, byte1, nibble);
|
ESP_LOGD(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, cmd_name(cmd), byte2, byte1, nibble);
|
||||||
|
|
||||||
if (cmd == command::STATUS) {
|
if (cmd == command::STATUS) {
|
||||||
auto doorState = static_cast<DoorState>(nibble);
|
|
||||||
if (doorState == DoorState::DOOR_STATE_CLOSED && this->doorState != doorState) {
|
this->doorState = static_cast<DoorState>(nibble);
|
||||||
transmit(command::GET_OPENINGS);
|
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPENING && this->previousDoorState==DoorState::DOOR_STATE_CLOSED) {
|
||||||
|
this->startOpening = millis();
|
||||||
|
}
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPEN && this->previousDoorState==DoorState::DOOR_STATE_OPENING) {
|
||||||
|
if (this->startOpening > 0) {
|
||||||
|
auto duration = (millis() - this->startOpening)/1000;
|
||||||
|
duration = this->openingDuration > 0 ? (duration + this->openingDuration)/2 : duration;
|
||||||
|
this->setOpeningDuration(round(duration*10)/10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_CLOSING && this->previousDoorState==DoorState::DOOR_STATE_OPEN) {
|
||||||
|
this->startClosing = millis();
|
||||||
|
}
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_CLOSED && this->previousDoorState==DoorState::DOOR_STATE_CLOSING) {
|
||||||
|
if (this->startClosing > 0) {
|
||||||
|
auto duration = (millis() - this->startClosing)/1000;
|
||||||
|
duration = this->closingDuration > 0 ? (duration + this->closingDuration)/2 : duration;
|
||||||
|
this->setClosingDuration(round(duration*10)/10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_STOPPED) {
|
||||||
|
this->startOpening = -1;
|
||||||
|
this->startClosing = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPEN) {
|
||||||
|
this->doorPosition = 1.0;
|
||||||
|
} else if (this->doorState == DoorState::DOOR_STATE_CLOSED) {
|
||||||
|
this->doorPosition = 0.0;
|
||||||
|
} else {
|
||||||
|
if (this->closingDuration == 0 || this->openingDuration == 0 || this->doorPosition == DOOR_POSITION_UNKNOWN) {
|
||||||
|
this->doorPosition = 0.5; // best guess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPENING && !this->movingToPosition) {
|
||||||
|
this->positionSyncWhileOpening(1.0 - this->doorPosition);
|
||||||
|
this->movingToPosition = true;
|
||||||
|
}
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_CLOSING && !this->movingToPosition) {
|
||||||
|
this->positionSyncWhileClosing(this->doorPosition);
|
||||||
|
this->movingToPosition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPEN || this->doorState == DoorState::DOOR_STATE_CLOSED || this->doorState == DoorState::DOOR_STATE_STOPPED) {
|
||||||
|
this->cancelPositionSyncCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->doorState = doorState;
|
|
||||||
this->lightState = static_cast<LightState>((byte2 >> 1) & 1);
|
this->lightState = static_cast<LightState>((byte2 >> 1) & 1);
|
||||||
this->lockState = static_cast<LockState>(byte2 & 1);
|
this->lockState = static_cast<LockState>(byte2 & 1);
|
||||||
this->motionState = MotionState::MOTION_STATE_CLEAR; // when the status message is read, reset motion state to 0|clear
|
this->motionState = MotionState::MOTION_STATE_CLEAR; // when the status message is read, reset motion state to 0|clear
|
||||||
this->motorState = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off
|
this->motorState = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off
|
||||||
// this->obstructionState = static_cast<ObstructionState>((byte1 >> 6) & 1);
|
// this->obstructionState = static_cast<ObstructionState>((byte1 >> 6) & 1);
|
||||||
ESP_LOGV(TAG, "Status: door=%s light=%s lock=%s",
|
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_CLOSED && this->doorState != this->previousDoorState) {
|
||||||
|
transmit(command::GET_OPENINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s",
|
||||||
door_state_to_string(this->doorState),
|
door_state_to_string(this->doorState),
|
||||||
light_state_to_string(this->lightState),
|
light_state_to_string(this->lightState),
|
||||||
lock_state_to_string(this->lockState));
|
lock_state_to_string(this->lockState));
|
||||||
|
@ -193,27 +256,27 @@ namespace ratgdo {
|
||||||
} else if (nibble == 2) { // toggle
|
} else if (nibble == 2) { // toggle
|
||||||
this->lightState = light_state_toggle(this->lightState);
|
this->lightState = light_state_toggle(this->lightState);
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "Light: action=%s state=%s",
|
ESP_LOGD(TAG, "Light: action=%s state=%s",
|
||||||
nibble == 0 ? "OFF" : nibble == 1 ? "ON"
|
nibble == 0 ? "OFF" : nibble == 1 ? "ON"
|
||||||
: "TOGGLE",
|
: "TOGGLE",
|
||||||
light_state_to_string(this->lightState));
|
light_state_to_string(this->lightState));
|
||||||
} else if (cmd == command::MOTOR_ON) {
|
} else if (cmd == command::MOTOR_ON) {
|
||||||
this->motorState = MotorState::MOTOR_STATE_ON;
|
this->motorState = MotorState::MOTOR_STATE_ON;
|
||||||
ESP_LOGV(TAG, "Motor: state=%s", motor_state_to_string(this->motorState));
|
ESP_LOGD(TAG, "Motor: state=%s", motor_state_to_string(this->motorState));
|
||||||
} else if (cmd == command::OPEN) {
|
} else if (cmd == command::OPEN) {
|
||||||
this->buttonState = (byte1 & 1) == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED;
|
this->buttonState = (byte1 & 1) == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED;
|
||||||
ESP_LOGV(TAG, "Open: button=%s", button_state_to_string(this->buttonState));
|
ESP_LOGD(TAG, "Open: button=%s", button_state_to_string(this->buttonState));
|
||||||
} else if (cmd == command::OPENINGS) {
|
} else if (cmd == command::OPENINGS) {
|
||||||
this->openings = (byte1 << 8) | byte2;
|
this->openings = (byte1 << 8) | byte2;
|
||||||
ESP_LOGV(TAG, "Openings: %d", this->openings);
|
ESP_LOGD(TAG, "Openings: %d", this->openings);
|
||||||
} else if (cmd == command::MOTION) {
|
} else if (cmd == command::MOTION) {
|
||||||
this->motionState = MotionState::MOTION_STATE_DETECTED;
|
this->motionState = MotionState::MOTION_STATE_DETECTED;
|
||||||
if (this->lightState == LightState::LIGHT_STATE_OFF) {
|
if (this->lightState == LightState::LIGHT_STATE_OFF) {
|
||||||
transmit(command::GET_STATUS);
|
transmit(command::GET_STATUS);
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "Motion: %s", motion_state_to_string(this->motionState));
|
ESP_LOGD(TAG, "Motion: %s", motion_state_to_string(this->motionState));
|
||||||
} else {
|
} 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, "Unhandled command: cmd=%03x nibble=%02x byte1=%02x byte2=%02x fixed=%010" PRIx64 " data=%08" PRIx32, cmd, nibble, byte1, byte2, fixed, data);
|
||||||
}
|
}
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +286,7 @@ namespace ratgdo {
|
||||||
uint64_t fixed = ((command & ~0xff) << 24) | this->remote_id;
|
uint64_t fixed = ((command & ~0xff) << 24) | this->remote_id;
|
||||||
uint32_t send_data = (data << 8) | (command & 0xff);
|
uint32_t send_data = (data << 8) | (command & 0xff);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), this->rollingCodeCounter, fixed, send_data);
|
ESP_LOGD(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), this->rollingCodeCounter, fixed, send_data);
|
||||||
encode_wireline(this->rollingCodeCounter, fixed, send_data, this->txRollingCode);
|
encode_wireline(this->rollingCodeCounter, fixed, send_data, this->txRollingCode);
|
||||||
|
|
||||||
printRollingCode();
|
printRollingCode();
|
||||||
|
@ -232,11 +295,51 @@ namespace ratgdo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::setOpeningDuration(float duration)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Set opening duration: %.1fs", duration);
|
||||||
|
this->openingDuration = duration;
|
||||||
|
this->openingDurationPref_.save(&this->openingDuration);
|
||||||
|
|
||||||
|
sendOpeningDuration();
|
||||||
|
|
||||||
|
if (this->closingDuration == 0 && duration != 0) {
|
||||||
|
this->setClosingDuration(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::sendOpeningDuration()
|
||||||
|
{
|
||||||
|
for (auto* child : this->children_) {
|
||||||
|
child->on_opening_duration_change(this->openingDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::setClosingDuration(float duration)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Set closing duration: %.1fs", duration);
|
||||||
|
this->closingDuration = duration;
|
||||||
|
this->closingDurationPref_.save(&this->closingDuration);
|
||||||
|
|
||||||
|
sendClosingDuration();
|
||||||
|
|
||||||
|
if (this->openingDuration == 0 && duration != 0) {
|
||||||
|
this->setOpeningDuration(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::sendClosingDuration()
|
||||||
|
{
|
||||||
|
for (auto* child : this->children_) {
|
||||||
|
child->on_closing_duration_change(this->closingDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RATGDOComponent::setRollingCodeCounter(uint32_t counter)
|
void RATGDOComponent::setRollingCodeCounter(uint32_t counter)
|
||||||
{
|
{
|
||||||
ESP_LOGV(TAG, "Set rolling code counter to %d", counter);
|
ESP_LOGV(TAG, "Set rolling code counter to %d", counter);
|
||||||
this->rollingCodeCounter = counter;
|
this->rollingCodeCounter = counter;
|
||||||
this->pref_.save(&this->rollingCodeCounter);
|
this->rollingCodePref_.save(&this->rollingCodeCounter);
|
||||||
sendRollingCodeChanged();
|
sendRollingCodeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,10 +473,17 @@ namespace ratgdo {
|
||||||
if (this->doorState != this->previousDoorState) {
|
if (this->doorState != this->previousDoorState) {
|
||||||
ESP_LOGV(TAG, "Door state: %s", door_state_to_string(this->doorState));
|
ESP_LOGV(TAG, "Door state: %s", door_state_to_string(this->doorState));
|
||||||
for (auto* child : this->children_) {
|
for (auto* child : this->children_) {
|
||||||
child->on_door_state(this->doorState);
|
child->on_door_state(this->doorState, this->doorPosition);
|
||||||
}
|
}
|
||||||
this->previousDoorState = this->doorState;
|
this->previousDoorState = this->doorState;
|
||||||
}
|
}
|
||||||
|
if (this->doorPosition != this->previousDoorPosition) {
|
||||||
|
ESP_LOGV(TAG, "Door position: %f", this->doorPosition);
|
||||||
|
for (auto* child : this->children_) {
|
||||||
|
child->on_door_state(this->doorState, this->doorPosition);
|
||||||
|
}
|
||||||
|
this->previousDoorPosition = this->doorPosition;
|
||||||
|
}
|
||||||
if (this->lightState != this->previousLightState) {
|
if (this->lightState != this->previousLightState) {
|
||||||
ESP_LOGV(TAG, "Light state %s (%d)", light_state_to_string(this->lightState), this->lightState);
|
ESP_LOGV(TAG, "Light state %s (%d)", light_state_to_string(this->lightState), this->lightState);
|
||||||
for (auto* child : this->children_) {
|
for (auto* child : this->children_) {
|
||||||
|
@ -485,18 +595,28 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::openDoor()
|
void RATGDOComponent::openDoor()
|
||||||
{
|
{
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPENING) {
|
||||||
|
return; // gets ignored by opener
|
||||||
|
}
|
||||||
|
this->cancelPositionSyncCallbacks();
|
||||||
|
|
||||||
doorCommand(data::DOOR_OPEN);
|
doorCommand(data::DOOR_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::closeDoor()
|
void RATGDOComponent::closeDoor()
|
||||||
{
|
{
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_CLOSING || this->doorState == DoorState::DOOR_STATE_OPENING) {
|
||||||
|
return; // gets ignored by opener
|
||||||
|
}
|
||||||
|
this->cancelPositionSyncCallbacks();
|
||||||
|
|
||||||
doorCommand(data::DOOR_CLOSE);
|
doorCommand(data::DOOR_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::stopDoor()
|
void RATGDOComponent::stopDoor()
|
||||||
{
|
{
|
||||||
if (this->doorState != DoorState::DOOR_STATE_OPENING && this->doorState != DoorState::DOOR_STATE_CLOSING) {
|
if (this->doorState != DoorState::DOOR_STATE_OPENING && this->doorState != DoorState::DOOR_STATE_CLOSING) {
|
||||||
ESP_LOGV(TAG, "The door is not moving.");
|
ESP_LOGW(TAG, "The door is not moving.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
doorCommand(data::DOOR_STOP);
|
doorCommand(data::DOOR_STOP);
|
||||||
|
@ -504,9 +624,98 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::toggleDoor()
|
void RATGDOComponent::toggleDoor()
|
||||||
{
|
{
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPENING) {
|
||||||
|
return; // gets ignored by opener
|
||||||
|
}
|
||||||
|
this->cancelPositionSyncCallbacks();
|
||||||
|
|
||||||
doorCommand(data::DOOR_TOGGLE);
|
doorCommand(data::DOOR_TOGGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RATGDOComponent::positionSyncWhileOpening(float delta, float update_period) {
|
||||||
|
if (this->openingDuration==0) {
|
||||||
|
ESP_LOGW(TAG, "I don't know opening duration, ignoring position sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto updates = this->openingDuration * 1000 * delta/update_period;
|
||||||
|
auto d = delta/updates;
|
||||||
|
auto count = int(updates);
|
||||||
|
ESP_LOGD(TAG, "[Opening] Position sync %d times: ", count);
|
||||||
|
// try to keep position in sync while door is moving
|
||||||
|
set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) {
|
||||||
|
ESP_LOGD(TAG, "[Opening] Position sync: %d: ", r);
|
||||||
|
this->doorPosition += d;
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::positionSyncWhileClosing(float delta, float update_period) {
|
||||||
|
if (this->closingDuration==0) {
|
||||||
|
ESP_LOGW(TAG, "I don't know closing duration, ignoring position sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto updates = this->closingDuration * 1000 * delta/update_period;
|
||||||
|
auto d = delta/updates;
|
||||||
|
auto count = int(updates);
|
||||||
|
ESP_LOGD(TAG, "[Closing] Position sync %d times: ", count);
|
||||||
|
// try to keep position in sync while door is moving
|
||||||
|
set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) {
|
||||||
|
ESP_LOGD(TAG, "[Closing] Position sync: %d: ", r);
|
||||||
|
this->doorPosition -= d;
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::setDoorPosition(float position)
|
||||||
|
{
|
||||||
|
if (this->doorState == DoorState::DOOR_STATE_OPENING || this->doorState == DoorState::DOOR_STATE_CLOSING) {
|
||||||
|
ESP_LOGW(TAG, "The door is moving, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = position - this->doorPosition;
|
||||||
|
if (delta > 0) { // open
|
||||||
|
if (this->openingDuration==0) {
|
||||||
|
ESP_LOGW(TAG, "I don't know opening duration, ignoring move to position");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto opening_time = this->openingDuration * 1000 * delta;
|
||||||
|
doorCommand(data::DOOR_OPEN);
|
||||||
|
this->movingToPosition = true;
|
||||||
|
set_timeout("move_to_position", opening_time, [=] {
|
||||||
|
doorCommand(data::DOOR_STOP);
|
||||||
|
this->movingToPosition = false;
|
||||||
|
this->doorPosition = position;
|
||||||
|
});
|
||||||
|
this->positionSyncWhileOpening(delta);
|
||||||
|
} else if (delta < 0) { // close
|
||||||
|
if (this->closingDuration==0) {
|
||||||
|
ESP_LOGW(TAG, "I don't know closing duration, ignoring move to position");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delta = -delta;
|
||||||
|
auto closing_time = this->closingDuration * 1000 * delta;
|
||||||
|
doorCommand(data::DOOR_CLOSE);
|
||||||
|
this->movingToPosition = true;
|
||||||
|
set_timeout("move_to_position", closing_time, [=] {
|
||||||
|
doorCommand(data::DOOR_STOP);
|
||||||
|
this->movingToPosition = false;
|
||||||
|
this->doorPosition = position;
|
||||||
|
});
|
||||||
|
this->positionSyncWhileClosing(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RATGDOComponent::cancelPositionSyncCallbacks() {
|
||||||
|
if (this->movingToPosition) {
|
||||||
|
ESP_LOGD(TAG, "Cancelling position callbacks");
|
||||||
|
cancel_timeout("move_to_position");
|
||||||
|
cancel_retry("position_sync_while_moving");
|
||||||
|
}
|
||||||
|
movingToPosition = false;
|
||||||
|
}
|
||||||
|
|
||||||
void RATGDOComponent::doorCommand(uint32_t data)
|
void RATGDOComponent::doorCommand(uint32_t data)
|
||||||
{
|
{
|
||||||
data |= (1 << 16); // button 1 ?
|
data |= (1 << 16); // button 1 ?
|
||||||
|
@ -556,7 +765,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::saveCounter()
|
void RATGDOComponent::saveCounter()
|
||||||
{
|
{
|
||||||
this->pref_.save(&this->rollingCodeCounter);
|
this->rollingCodePref_.save(&this->rollingCodeCounter);
|
||||||
// Forcing a sync results in a soft reset if there are too many
|
// Forcing a sync results in a soft reset if there are too many
|
||||||
// writes to flash in a short period of time. To avoid this,
|
// writes to flash in a short period of time. To avoid this,
|
||||||
// we have configured preferences to write every 5s
|
// we have configured preferences to write every 5s
|
||||||
|
|
|
@ -33,6 +33,8 @@ namespace ratgdo {
|
||||||
|
|
||||||
static const uint8_t CODE_LENGTH = 19;
|
static const uint8_t CODE_LENGTH = 19;
|
||||||
|
|
||||||
|
const float DOOR_POSITION_UNKNOWN = -1.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
|
from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
|
||||||
_WIRELINE_COMMANDS = {
|
_WIRELINE_COMMANDS = {
|
||||||
|
@ -129,6 +131,11 @@ namespace ratgdo {
|
||||||
uint32_t rollingCodeCounter { 0 };
|
uint32_t rollingCodeCounter { 0 };
|
||||||
uint32_t lastSyncedRollingCodeCounter { 0 };
|
uint32_t lastSyncedRollingCodeCounter { 0 };
|
||||||
|
|
||||||
|
float startOpening { -1 };
|
||||||
|
float openingDuration { 0 };
|
||||||
|
float startClosing { -1 };
|
||||||
|
float closingDuration { 0 };
|
||||||
|
|
||||||
uint8_t txRollingCode[CODE_LENGTH];
|
uint8_t txRollingCode[CODE_LENGTH];
|
||||||
uint8_t rxRollingCode[CODE_LENGTH];
|
uint8_t rxRollingCode[CODE_LENGTH];
|
||||||
|
|
||||||
|
@ -138,6 +145,10 @@ namespace ratgdo {
|
||||||
DoorState previousDoorState { DoorState::DOOR_STATE_UNKNOWN };
|
DoorState previousDoorState { DoorState::DOOR_STATE_UNKNOWN };
|
||||||
DoorState doorState { DoorState::DOOR_STATE_UNKNOWN };
|
DoorState doorState { DoorState::DOOR_STATE_UNKNOWN };
|
||||||
|
|
||||||
|
float doorPosition { DOOR_POSITION_UNKNOWN };
|
||||||
|
float previousDoorPosition { DOOR_POSITION_UNKNOWN };
|
||||||
|
bool movingToPosition { false };
|
||||||
|
|
||||||
LightState previousLightState { LightState::LIGHT_STATE_UNKNOWN };
|
LightState previousLightState { LightState::LIGHT_STATE_UNKNOWN };
|
||||||
LightState lightState { LightState::LIGHT_STATE_UNKNOWN };
|
LightState lightState { LightState::LIGHT_STATE_UNKNOWN };
|
||||||
|
|
||||||
|
@ -179,6 +190,10 @@ namespace ratgdo {
|
||||||
void openDoor();
|
void openDoor();
|
||||||
void closeDoor();
|
void closeDoor();
|
||||||
void stopDoor();
|
void stopDoor();
|
||||||
|
void setDoorPosition(float position);
|
||||||
|
void positionSyncWhileOpening(float delta, float update_period = 500);
|
||||||
|
void positionSyncWhileClosing(float delta, float update_period = 500);
|
||||||
|
void cancelPositionSyncCallbacks();
|
||||||
|
|
||||||
void toggleLight();
|
void toggleLight();
|
||||||
void lightOn();
|
void lightOn();
|
||||||
|
@ -197,12 +212,20 @@ namespace ratgdo {
|
||||||
void incrementRollingCodeCounter();
|
void incrementRollingCodeCounter();
|
||||||
void sendRollingCodeChanged();
|
void sendRollingCodeChanged();
|
||||||
void setRollingCodeCounter(uint32_t counter);
|
void setRollingCodeCounter(uint32_t counter);
|
||||||
|
|
||||||
|
void setOpeningDuration(float duration);
|
||||||
|
void sendOpeningDuration();
|
||||||
|
void setClosingDuration(float duration);
|
||||||
|
void sendClosingDuration();
|
||||||
|
|
||||||
LightState getLightState();
|
LightState getLightState();
|
||||||
/** Register a child component. */
|
/** Register a child component. */
|
||||||
void register_child(RATGDOClient* obj);
|
void register_child(RATGDOClient* obj);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ESPPreferenceObject pref_;
|
ESPPreferenceObject rollingCodePref_;
|
||||||
|
ESPPreferenceObject openingDurationPref_;
|
||||||
|
ESPPreferenceObject closingDurationPref_;
|
||||||
std::vector<RATGDOClient*> children_;
|
std::vector<RATGDOClient*> children_;
|
||||||
bool rollingCodeUpdatesEnabled_ { true };
|
bool rollingCodeUpdatesEnabled_ { true };
|
||||||
bool forceUpdate_ { false };
|
bool forceUpdate_ { false };
|
||||||
|
|
|
@ -6,13 +6,15 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ratgdo {
|
namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOClient::on_door_state(DoorState state) {};
|
void RATGDOClient::on_door_state(DoorState state, float position) {};
|
||||||
void RATGDOClient::on_light_state(LightState state) {};
|
void RATGDOClient::on_light_state(LightState state) {};
|
||||||
void RATGDOClient::on_lock_state(LockState state) {};
|
void RATGDOClient::on_lock_state(LockState state) {};
|
||||||
void RATGDOClient::on_motion_state(MotionState state) {};
|
void RATGDOClient::on_motion_state(MotionState state) {};
|
||||||
void RATGDOClient::on_obstruction_state(ObstructionState state) {};
|
void RATGDOClient::on_obstruction_state(ObstructionState state) {};
|
||||||
void RATGDOClient::on_motor_state(MotorState state) {};
|
void RATGDOClient::on_motor_state(MotorState state) {};
|
||||||
void RATGDOClient::on_rolling_code_change(uint32_t rollingCodeCounter) {};
|
void RATGDOClient::on_rolling_code_change(uint32_t rollingCodeCounter) {};
|
||||||
|
void RATGDOClient::on_opening_duration_change(float duration) {};
|
||||||
|
void RATGDOClient::on_closing_duration_change(float duration) {};
|
||||||
void RATGDOClient::on_openings_change(uint32_t openings) {};
|
void RATGDOClient::on_openings_change(uint32_t openings) {};
|
||||||
void RATGDOClient::on_button_state(ButtonState state) {};
|
void RATGDOClient::on_button_state(ButtonState state) {};
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,15 @@ namespace ratgdo {
|
||||||
|
|
||||||
class RATGDOClient : public Parented<RATGDOComponent> {
|
class RATGDOClient : public Parented<RATGDOComponent> {
|
||||||
public:
|
public:
|
||||||
virtual void on_door_state(DoorState state);
|
virtual void on_door_state(DoorState state, float position);
|
||||||
virtual void on_light_state(LightState state);
|
virtual void on_light_state(LightState state);
|
||||||
virtual void on_lock_state(LockState state);
|
virtual void on_lock_state(LockState state);
|
||||||
virtual void on_motion_state(MotionState state);
|
virtual void on_motion_state(MotionState state);
|
||||||
virtual void on_obstruction_state(ObstructionState state);
|
virtual void on_obstruction_state(ObstructionState state);
|
||||||
virtual void on_motor_state(MotorState state);
|
virtual void on_motor_state(MotorState state);
|
||||||
virtual void on_rolling_code_change(uint32_t rollingCodeCounter);
|
virtual void on_rolling_code_change(uint32_t rollingCodeCounter);
|
||||||
|
virtual void on_opening_duration_change(float duration);
|
||||||
|
virtual void on_closing_duration_change(float duration);
|
||||||
virtual void on_openings_change(uint32_t openings);
|
virtual void on_openings_change(uint32_t openings);
|
||||||
virtual void on_button_state(ButtonState state);
|
virtual void on_button_state(ButtonState state);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue