Skip to content

Commit

Permalink
Add ABB-Welcome / Busch-Welcome Door Intercom Protocol (#4689)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
  • Loading branch information
Mat931 and jesserockz committed Apr 9, 2024
1 parent 5441213 commit 3b6e8fa
Show file tree
Hide file tree
Showing 3 changed files with 476 additions and 0 deletions.
102 changes: 102 additions & 0 deletions esphome/components/remote_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1718,3 +1718,105 @@ async def haier_action(var, config, args):
vec_ = cg.std_vector.template(cg.uint8)
template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_)
cg.add(var.set_code(template_))


# ABBWelcome
(
ABBWelcomeData,
ABBWelcomeBinarySensor,
ABBWelcomeTrigger,
ABBWelcomeAction,
ABBWelcomeDumper,
) = declare_protocol("ABBWelcome")

CONF_SOURCE_ADDRESS = "source_address"
CONF_DESTINATION_ADDRESS = "destination_address"
CONF_THREE_BYTE_ADDRESS = "three_byte_address"
CONF_MESSAGE_TYPE = "message_type"
CONF_MESSAGE_ID = "message_id"
CONF_RETRANSMISSION = "retransmission"

ABB_WELCOME_SCHEMA = cv.Schema(
{
cv.Required(CONF_SOURCE_ADDRESS): cv.hex_uint32_t,
cv.Required(CONF_DESTINATION_ADDRESS): cv.hex_uint32_t,
cv.Optional(CONF_RETRANSMISSION, default=False): cv.boolean,
cv.Optional(CONF_THREE_BYTE_ADDRESS, default=False): cv.boolean,
cv.Required(CONF_MESSAGE_TYPE): cv.Any(cv.hex_uint8_t, cv.uint8_t),
cv.Optional(CONF_MESSAGE_ID): cv.Any(cv.hex_uint8_t, cv.uint8_t),
cv.Optional(CONF_DATA): cv.All(
[cv.Any(cv.hex_uint8_t, cv.uint8_t)],
cv.Length(min=0, max=7),
),
}
)


@register_binary_sensor("abbwelcome", ABBWelcomeBinarySensor, ABB_WELCOME_SCHEMA)
def abbwelcome_binary_sensor(var, config):
cg.add(var.set_three_byte_address(config[CONF_THREE_BYTE_ADDRESS]))
cg.add(var.set_source_address(config[CONF_SOURCE_ADDRESS]))
cg.add(var.set_destination_address(config[CONF_DESTINATION_ADDRESS]))
cg.add(var.set_retransmission(config[CONF_RETRANSMISSION]))
cg.add(var.set_message_type(config[CONF_MESSAGE_TYPE]))
cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config))
if CONF_MESSAGE_ID in config:
cg.add(var.set_message_id(config[CONF_MESSAGE_ID]))
if CONF_DATA in config:
cg.add(var.set_data(config[CONF_DATA]))
cg.add(var.finalize())


@register_trigger("abbwelcome", ABBWelcomeTrigger, ABBWelcomeData)
def abbwelcome_trigger(var, config):
pass


@register_dumper("abbwelcome", ABBWelcomeDumper)
def abbwelcome_dumper(var, config):
pass


@register_action("abbwelcome", ABBWelcomeAction, ABB_WELCOME_SCHEMA)
async def abbwelcome_action(var, config, args):
cg.add(
var.set_three_byte_address(
await cg.templatable(config[CONF_THREE_BYTE_ADDRESS], args, cg.bool_)
)
)
cg.add(
var.set_source_address(
await cg.templatable(config[CONF_SOURCE_ADDRESS], args, cg.uint16)
)
)
cg.add(
var.set_destination_address(
await cg.templatable(config[CONF_DESTINATION_ADDRESS], args, cg.uint16)
)
)
cg.add(
var.set_retransmission(
await cg.templatable(config[CONF_RETRANSMISSION], args, cg.bool_)
)
)
cg.add(
var.set_message_type(
await cg.templatable(config[CONF_MESSAGE_TYPE], args, cg.uint8)
)
)
cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config))
if CONF_MESSAGE_ID in config:
cg.add(
var.set_message_id(
await cg.templatable(config[CONF_MESSAGE_ID], args, cg.uint8)
)
)
if CONF_DATA in config:
data_ = config[CONF_DATA]
if cg.is_template(data_):
template_ = await cg.templatable(
data_, args, cg.std_vector.template(cg.uint8)
)
cg.add(var.set_data_template(template_))
else:
cg.add(var.set_data_static(data_))
123 changes: 123 additions & 0 deletions esphome/components/remote_base/abbwelcome_protocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "abbwelcome_protocol.h"
#include "esphome/core/log.h"

namespace esphome {
namespace remote_base {

static const char *const TAG = "remote.abbwelcome";

static const uint32_t BIT_ONE_SPACE_US = 102;
static const uint32_t BIT_ZERO_MARK_US = 32; // 18-44
static const uint32_t BIT_ZERO_SPACE_US = BIT_ONE_SPACE_US - BIT_ZERO_MARK_US;
static const uint16_t BYTE_SPACE_US = 210;

uint8_t ABBWelcomeData::calc_cs_() const {
uint8_t checksum = 0;
for (uint8_t i = 0; i < this->size() - 1; i++) {
uint16_t temp = checksum ^ (this->data_[i]);
temp = temp ^ (uint16_t) (((uint32_t) temp << 0x11) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x12) >> 0x10) ^
(uint16_t) (((uint32_t) temp << 0x13) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x14) >> 0x10) ^
(uint16_t) (((uint32_t) temp << 0x15) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x16) >> 0x10) ^
(uint16_t) (((uint32_t) temp << 0x17) >> 0x10);
checksum = (temp & 0xfe) ^ ((temp >> 8) & 1);
}
return ~checksum;
}

void ABBWelcomeProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t data) const {
// space = bus high, mark = activate bus pulldown
dst->mark(BIT_ZERO_MARK_US);
uint32_t next_space = BIT_ZERO_SPACE_US;
for (uint8_t mask = 1 << 7; mask; mask >>= 1) {
if (data & mask) {
next_space += BIT_ONE_SPACE_US;
} else {
dst->space(next_space);
dst->mark(BIT_ZERO_MARK_US);
next_space = BIT_ZERO_SPACE_US;
}
}
next_space += BYTE_SPACE_US;
dst->space(next_space);
}

void ABBWelcomeProtocol::encode(RemoteTransmitData *dst, const ABBWelcomeData &src) {
dst->set_carrier_frequency(0);
uint32_t reserve_count = 0;
for (size_t i = 0; i < src.size(); i++) {
reserve_count += 2 * (9 - (src[i] & 1) - ((src[i] >> 1) & 1) - ((src[i] >> 2) & 1) - ((src[i] >> 3) & 1) -
((src[i] >> 4) & 1) - ((src[i] >> 5) & 1) - ((src[i] >> 6) & 1) - ((src[i] >> 7) & 1));
}
dst->reserve(reserve_count);
for (size_t i = 0; i < src.size(); i++)
this->encode_byte_(dst, src[i]);
ESP_LOGD(TAG, "Transmitting: %s", src.to_string().c_str());
}

bool ABBWelcomeProtocol::decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data) {
if (!src.expect_mark(BIT_ZERO_MARK_US))
return false;
uint32_t next_space = BIT_ZERO_SPACE_US;
for (uint8_t mask = 1 << 7; mask; mask >>= 1) {
// if (!src.peek_space_at_least(next_space, 0))
// return false;
if (src.expect_space(next_space)) {
if (!src.expect_mark(BIT_ZERO_MARK_US))
return false;
next_space = BIT_ZERO_SPACE_US;
} else {
data |= mask;
next_space += BIT_ONE_SPACE_US;
}
}
next_space += BYTE_SPACE_US;
// if (!src.peek_space_at_least(next_space, 0))
// return false;
done = !(src.expect_space(next_space));
return true;
}

optional<ABBWelcomeData> ABBWelcomeProtocol::decode(RemoteReceiveData src) {
if (src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US) &&
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US + BYTE_SPACE_US) &&
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + 8 * BIT_ONE_SPACE_US + BYTE_SPACE_US)) {
ESP_LOGVV(TAG, "Received Header: 0x55FF");
ABBWelcomeData out;
out[0] = 0x55;
out[1] = 0xff;
bool done = false;
uint8_t length = 10;
uint8_t received_bytes = 2;
for (; (received_bytes < length) && !done; received_bytes++) {
uint8_t data = 0;
if (!this->decode_byte_(src, done, data)) {
ESP_LOGW(TAG, "Received incomplete packet: %s", out.to_string(received_bytes).c_str());
return {};
}
if (received_bytes == 2) {
length += std::min(static_cast<uint8_t>(data & DATA_LENGTH_MASK), MAX_DATA_LENGTH);
if (data & 0x40) {
length += 2;
}
}
ESP_LOGVV(TAG, "Received Byte: 0x%02X", data);
out[received_bytes] = data;
}
if (out.is_valid()) {
ESP_LOGI(TAG, "Received: %s", out.to_string().c_str());
return out;
}
ESP_LOGW(TAG, "Received malformed packet: %s", out.to_string(received_bytes).c_str());
}
return {};
}

void ABBWelcomeProtocol::dump(const ABBWelcomeData &data) {
ESP_LOGD(TAG, "Received ABBWelcome: %s", data.to_string().c_str());
}

} // namespace remote_base
} // namespace esphome

0 comments on commit 3b6e8fa

Please sign in to comment.