mirror of
https://github.com/esphome/esphome.git
synced 2025-10-08 04:43:46 +01:00
🏗 Merge C++ into python codebase (#504)
## Description: Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97 Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍 Progress: - Core support (file copy etc): 80% - Base Abstractions (light, switch): ~50% - Integrations: ~10% - Working? Yes, (but only with ported components). Other refactors: - Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`) - Rework coroutine syntax - Move from `component/platform.py` to `domain/component.py` structure as with HA - Move all defaults out of C++ and into config validation. - Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration. - Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit. Future work: - Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block - Enable loading from `custom_components` folder. **Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97 **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
This commit is contained in:
658
esphome/components/remote_base/__init__.py
Normal file
658
esphome/components/remote_base/__init__.py
Normal file
@@ -0,0 +1,658 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.automation import ACTION_REGISTRY
|
||||
from esphome.components import binary_sensor as binary_sensor_
|
||||
from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID, CONF_NBITS, CONF_ADDRESS, \
|
||||
CONF_COMMAND, CONF_CODE, CONF_PULSE_LENGTH, CONF_SYNC, CONF_ZERO, CONF_ONE, CONF_INVERTED, \
|
||||
CONF_PROTOCOL, CONF_GROUP, CONF_DEVICE, CONF_STATE, CONF_CHANNEL, CONF_FAMILY, CONF_REPEAT, \
|
||||
CONF_WAIT_TIME, CONF_TIMES
|
||||
from esphome.core import coroutine
|
||||
from esphome.py_compat import string_types, text_type
|
||||
from esphome.util import ServiceRegistry
|
||||
|
||||
AUTO_LOAD = ['binary_sensor']
|
||||
|
||||
CONF_RECEIVER_ID = 'receiver_id'
|
||||
CONF_TRANSMITTER_ID = 'transmitter_id'
|
||||
|
||||
ns = remote_base_ns = cg.esphome_ns.namespace('remote_base')
|
||||
RemoteProtocol = ns.class_('RemoteProtocol')
|
||||
RemoteReceiverListener = ns.class_('RemoteReceiverListener')
|
||||
RemoteReceiverBinarySensorBase = ns.class_('RemoteReceiverBinarySensorBase',
|
||||
binary_sensor_.BinarySensor, cg.Component)
|
||||
RemoteReceiverTrigger = ns.class_('RemoteReceiverTrigger', cg.Trigger, RemoteReceiverListener)
|
||||
RemoteTransmitterDumper = ns.class_('RemoteTransmitterDumper')
|
||||
RemoteTransmitterActionBase = ns.class_('RemoteTransmitterActionBase', cg.Action)
|
||||
RemoteReceiverBase = ns.class_('RemoteReceiverBase')
|
||||
RemoteTransmitterBase = ns.class_('RemoteTransmitterBase')
|
||||
|
||||
|
||||
def templatize(value):
|
||||
if isinstance(value, cv.Schema):
|
||||
value = value.schema
|
||||
ret = {}
|
||||
for key, val in value.items():
|
||||
ret[key] = cv.templatable(val)
|
||||
return cv.Schema(ret)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_listener(var, config):
|
||||
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
||||
cg.add(receiver.register_listener(var))
|
||||
|
||||
|
||||
def register_binary_sensor(name, type, schema):
|
||||
if not isinstance(schema, cv.Schema):
|
||||
schema = cv.Schema(schema)
|
||||
validator = schema.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(type),
|
||||
cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase),
|
||||
})
|
||||
registerer = BINARY_SENSOR_REGISTRY.register(name, validator)
|
||||
|
||||
def decorator(func):
|
||||
@coroutine
|
||||
def new_func(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield register_listener(var, config)
|
||||
yield coroutine(func)(var, config)
|
||||
yield var
|
||||
|
||||
return registerer(new_func)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def register_trigger(name, type, data_type):
|
||||
validator = automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(type),
|
||||
cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase),
|
||||
})
|
||||
registerer = TRIGGER_REGISTRY.register('on_{}'.format(name), validator)
|
||||
|
||||
def decorator(func):
|
||||
@coroutine
|
||||
def new_func(config):
|
||||
var = cg.new_Pvariable(config[CONF_TRIGGER_ID])
|
||||
yield register_listener(var, config)
|
||||
yield coroutine(func)(var, config)
|
||||
yield automation.build_automation(var, [(data_type, 'x')], config)
|
||||
yield var
|
||||
|
||||
return registerer(new_func)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def register_dumper(name, type):
|
||||
validator = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(type),
|
||||
cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase),
|
||||
})
|
||||
registerer = DUMPER_REGISTRY.register(name, validator)
|
||||
|
||||
def decorator(func):
|
||||
@coroutine
|
||||
def new_func(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
||||
cg.add(receiver.register_dumper(var))
|
||||
yield coroutine(func)(var, config)
|
||||
yield var
|
||||
|
||||
return registerer(new_func)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def register_action(name, type_, schema):
|
||||
validator = templatize(schema).extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(type_),
|
||||
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_variable_id(RemoteTransmitterBase),
|
||||
cv.Optional(CONF_REPEAT): cv.Schema({
|
||||
cv.Required(CONF_TIMES): cv.templatable(cv.positive_int),
|
||||
cv.Optional(CONF_WAIT_TIME, default='10ms'):
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
}),
|
||||
})
|
||||
registerer = ACTION_REGISTRY.register('remote_transmitter.transmit_{}'.format(name), validator)
|
||||
|
||||
def decorator(func):
|
||||
@coroutine
|
||||
def new_func(config, action_id, template_arg, args):
|
||||
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||
type = type_.template(template_arg)
|
||||
var = cg.Pvariable(action_id, type.new(), type=type)
|
||||
cg.add(var.set_parent(transmitter))
|
||||
if CONF_REPEAT in config:
|
||||
conf = config[CONF_REPEAT]
|
||||
template_ = yield cg.templatable(conf[CONF_TIMES], args, cg.uint32)
|
||||
cg.add(var.set_send_times(template_))
|
||||
template_ = yield cg.templatable(conf[CONF_WAIT_TIME], args, cg.uint32)
|
||||
cg.add(var.set_send_wait(template_))
|
||||
yield coroutine(func)(var, config, args)
|
||||
yield var
|
||||
|
||||
return registerer(new_func)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def declare_protocol(name):
|
||||
data = ns.struct('{}Data'.format(name))
|
||||
protocol = ns.class_('{}Protocol'.format(name))
|
||||
binary_sensor = ns.class_('{}BinarySensor'.format(name), RemoteReceiverBinarySensorBase)
|
||||
trigger = ns.class_('{}Trigger'.format(name), RemoteReceiverTrigger)
|
||||
action = ns.class_('{}Action'.format(name), RemoteTransmitterActionBase)
|
||||
dumper = ns.class_('{}Dumper'.format(name), RemoteTransmitterDumper)
|
||||
return data, protocol, binary_sensor, trigger, action, dumper
|
||||
|
||||
|
||||
BINARY_SENSOR_REGISTRY = ServiceRegistry()
|
||||
TRIGGER_REGISTRY = ServiceRegistry()
|
||||
DUMPER_REGISTRY = ServiceRegistry()
|
||||
|
||||
|
||||
def validate_dumpers(value):
|
||||
if isinstance(value, string_types) and value.lower() == 'all':
|
||||
return validate_dumpers(list(DUMPER_REGISTRY.keys()))
|
||||
return cv.validate_registry('dumper', DUMPER_REGISTRY, [])(value)
|
||||
|
||||
|
||||
def validate_binary_sensor(base_schema):
|
||||
validator = cv.validate_registry_entry('remote receiver', BINARY_SENSOR_REGISTRY,
|
||||
cv.extract_keys(base_schema))
|
||||
return validator
|
||||
|
||||
|
||||
def validate_triggers(base_schema):
|
||||
assert isinstance(base_schema, cv.Schema)
|
||||
|
||||
def validator(config):
|
||||
added_keys = {}
|
||||
for key, (valid, _) in TRIGGER_REGISTRY.items():
|
||||
added_keys[cv.Optional(key)] = valid
|
||||
new_schema = base_schema.extend(added_keys)
|
||||
return new_schema(config)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_binary_sensor(config):
|
||||
var = yield cg.build_registry_entry(BINARY_SENSOR_REGISTRY, config)
|
||||
yield var
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_triggers(full_config):
|
||||
for key in TRIGGER_REGISTRY:
|
||||
for config in full_config.get(key, []):
|
||||
func = TRIGGER_REGISTRY[key][1]
|
||||
yield func(config)
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_dumpers(config):
|
||||
yield cg.build_registry_list(DUMPER_REGISTRY, config)
|
||||
|
||||
|
||||
# JVC
|
||||
JVCData, JVCProtocol, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol('JVC')
|
||||
JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t})
|
||||
|
||||
|
||||
@register_binary_sensor('jvc', JVCBinarySensor, JVC_SCHEMA)
|
||||
def jvc_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
JVCData,
|
||||
('data', config[CONF_DATA]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('jvc', JVCTrigger, JVCData)
|
||||
def jvc_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('jvc', JVCDumper)
|
||||
def jvc_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('jvc', JVCAction, JVC_SCHEMA)
|
||||
def jvc_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint32)
|
||||
cg.add(var.set_data(template_))
|
||||
|
||||
|
||||
# LG
|
||||
LGData, LGProtocol, LGBinarySensor, LGTrigger, LGAction, LGDumper = declare_protocol('LG')
|
||||
LG_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True),
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('lg', LGBinarySensor, LG_SCHEMA)
|
||||
def lg_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
LGData,
|
||||
('data', config[CONF_DATA]),
|
||||
('nbits', config[CONF_NBITS]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('lg', LGTrigger, LGData)
|
||||
def lg_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('lg', LGDumper)
|
||||
def lg_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('lg', LGAction, LG_SCHEMA)
|
||||
def lg_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint32)
|
||||
cg.add(var.set_data(template_))
|
||||
template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint8)
|
||||
cg.add(var.set_nbits(template_))
|
||||
|
||||
|
||||
# NEC
|
||||
NECData, NECProtocol, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol('NEC')
|
||||
NEC_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
cv.Required(CONF_COMMAND): cv.hex_uint16_t,
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('nec', NECBinarySensor, NEC_SCHEMA)
|
||||
def nec_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
NECData,
|
||||
('address', config[CONF_ADDRESS]),
|
||||
('command', config[CONF_COMMAND]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('nec', NECTrigger, NECData)
|
||||
def nec_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('nec', NECDumper)
|
||||
def nec_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('nec', NECAction, NEC_SCHEMA)
|
||||
def nec_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16)
|
||||
cg.add(var.set_address(template_))
|
||||
template_ = yield cg.templatable(config[CONF_COMMAND], args, cg.uint16)
|
||||
cg.add(var.set_command(template_))
|
||||
|
||||
|
||||
# Sony
|
||||
SonyData, SonyProtocol, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol(
|
||||
'Sony')
|
||||
SONY_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True),
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('sony', SonyBinarySensor, SONY_SCHEMA)
|
||||
def sony_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
SonyData,
|
||||
('data', config[CONF_DATA]),
|
||||
('nbits', config[CONF_NBITS]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('sony', SonyTrigger, SonyData)
|
||||
def sony_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('sony', SonyDumper)
|
||||
def sony_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('sony', SonyAction, SONY_SCHEMA)
|
||||
def sony_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint16)
|
||||
cg.add(var.set_data(template_))
|
||||
template_ = yield cg.templatable(config[CONF_NBITS], args, cg.uint32)
|
||||
cg.add(var.set_nbits(template_))
|
||||
|
||||
|
||||
# Raw
|
||||
def validate_raw_alternating(value):
|
||||
assert isinstance(value, list)
|
||||
last_negative = None
|
||||
for i, val in enumerate(value):
|
||||
this_negative = val < 0
|
||||
if i != 0:
|
||||
if this_negative == last_negative:
|
||||
raise cv.Invalid("Values must alternate between being positive and negative, "
|
||||
"please see index {} and {}".format(i, i + 1), [i])
|
||||
last_negative = this_negative
|
||||
return value
|
||||
|
||||
|
||||
RawData, RawProtocol, RawBinarySensor, RawTrigger, RawAction, RawDumper = declare_protocol('Raw')
|
||||
CONF_CODE_STORAGE_ID = 'code_storage_id'
|
||||
RAW_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_CODE): cv.All([cv.Any(cv.int_, cv.time_period_microseconds)],
|
||||
cv.Length(min=1), validate_raw_alternating),
|
||||
cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_variable_id(cg.int32),
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('raw', RawBinarySensor, RAW_SCHEMA)
|
||||
def raw_binary_sensor(var, config):
|
||||
code_ = config[CONF_CODE]
|
||||
arr = cg.progmem_array(config[CONF_ID], code_)
|
||||
cg.add(var.set_data(arr))
|
||||
cg.add(var.set_len(len(code_)))
|
||||
|
||||
|
||||
@register_trigger('raw', RawTrigger, cg.std_vector.template(cg.int32))
|
||||
def raw_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('raw', RawDumper)
|
||||
def raw_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('raw', RawAction, RAW_SCHEMA)
|
||||
def raw_action(var, config, args):
|
||||
code_ = config[CONF_CODE]
|
||||
if cg.is_template(code_):
|
||||
template_ = yield cg.templatable(code_, args, cg.std_vector.template(cg.int32))
|
||||
cg.add(var.set_code_template(template_))
|
||||
else:
|
||||
code_ = config[CONF_CODE]
|
||||
arr = cg.progmem_array(config[CONF_CODE_STORAGE_ID], code_)
|
||||
cg.add(var.set_code_static(arr, len(code_)))
|
||||
|
||||
|
||||
# RC5
|
||||
RC5Data, RC5Protocol, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol('RC5')
|
||||
RC5_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0x1F)),
|
||||
cv.Required(CONF_COMMAND): cv.All(cv.hex_int, cv.Range(min=0, max=0x3F)),
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('rc5', RC5BinarySensor, RC5_SCHEMA)
|
||||
def rc5_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
RC5Data,
|
||||
('address', config[CONF_ADDRESS]),
|
||||
('command', config[CONF_COMMAND]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('rc5', RC5Trigger, RC5Data)
|
||||
def rc5_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('rc5', RC5Dumper)
|
||||
def rc5_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('rc5', RC5Action, RC5_SCHEMA)
|
||||
def rc5_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
|
||||
cg.add(var.set_address(template_))
|
||||
template_ = yield cg.templatable(config[CONF_COMMAND], args, cg.uint8)
|
||||
cg.add(var.set_command(template_))
|
||||
|
||||
|
||||
# RC Switch Raw
|
||||
RC_SWITCH_TIMING_SCHEMA = cv.All([cv.uint8_t], cv.Length(min=2, max=2))
|
||||
|
||||
RC_SWITCH_PROTOCOL_SCHEMA = cv.Any(
|
||||
cv.All(cv.Coerce(int), cv.Range(min=1, max=7)),
|
||||
cv.Schema({
|
||||
cv.Required(CONF_PULSE_LENGTH): cv.uint32_t,
|
||||
cv.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA,
|
||||
cv.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA,
|
||||
cv.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA,
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
def validate_rc_switch_code(value):
|
||||
if not isinstance(value, (str, text_type)):
|
||||
raise cv.Invalid("All RCSwitch codes must be in quotes ('')")
|
||||
for c in value:
|
||||
if c not in ('0', '1'):
|
||||
raise cv.Invalid(u"Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed"
|
||||
u"".format(c))
|
||||
if len(value) > 32:
|
||||
raise cv.Invalid("Maximum length for RCSwitch codes is 32, code '{}' has length {}"
|
||||
"".format(value, len(value)))
|
||||
if not value:
|
||||
raise cv.Invalid("RCSwitch code must not be empty")
|
||||
return value
|
||||
|
||||
|
||||
def build_rc_switch_protocol(config):
|
||||
if isinstance(config, int):
|
||||
return rc_switch_protocols[config]
|
||||
pl = config[CONF_PULSE_LENGTH]
|
||||
return RCSwitchBase(config[CONF_SYNC][0] * pl, config[CONF_SYNC][1] * pl,
|
||||
config[CONF_ZERO][0] * pl, config[CONF_ZERO][1] * pl,
|
||||
config[CONF_ONE][0] * pl, config[CONF_ONE][1] * pl,
|
||||
config[CONF_INVERTED])
|
||||
|
||||
|
||||
RC_SWITCH_RAW_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_CODE): validate_rc_switch_code,
|
||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_A_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_GROUP): cv.All(validate_rc_switch_code, cv.Length(min=5, max=5)),
|
||||
cv.Required(CONF_DEVICE): cv.All(validate_rc_switch_code, cv.Length(min=5, max=5)),
|
||||
cv.Required(CONF_STATE): cv.boolean,
|
||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_B_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ADDRESS): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
||||
cv.Required(CONF_CHANNEL): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
||||
cv.Required(CONF_STATE): cv.boolean,
|
||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_C_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
||||
'l', 'm', 'n', 'o', 'p', lower=True),
|
||||
cv.Required(CONF_GROUP): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
||||
cv.Required(CONF_DEVICE): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
||||
cv.Required(CONF_STATE): cv.boolean,
|
||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_D_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True),
|
||||
cv.Required(CONF_DEVICE): cv.All(cv.uint8_t, cv.Range(min=1, max=3)),
|
||||
cv.Required(CONF_STATE): cv.boolean,
|
||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
|
||||
rc_switch_protocols = ns.rc_switch_protocols
|
||||
RCSwitchBase = ns.class_('RCSwitchBase')
|
||||
RCSwitchDumper = ns.class_('RCSwitchDumper', RemoteTransmitterDumper)
|
||||
RCSwitchRawAction = ns.class_('RCSwitchRawAction', RemoteTransmitterActionBase)
|
||||
RCSwitchTypeAAction = ns.class_('RCSwitchTypeAAction', RemoteTransmitterActionBase)
|
||||
RCSwitchTypeBAction = ns.class_('RCSwitchTypeBAction', RemoteTransmitterActionBase)
|
||||
RCSwitchTypeCAction = ns.class_('RCSwitchTypeCAction', RemoteTransmitterActionBase)
|
||||
RCSwitchTypeDAction = ns.class_('RCSwitchTypeDAction', RemoteTransmitterActionBase)
|
||||
RCSwitchRawReceiver = ns.class_('RCSwitchRawReceiver', RemoteReceiverBinarySensorBase)
|
||||
|
||||
|
||||
@register_binary_sensor('rc_switch_raw', RCSwitchRawReceiver, RC_SWITCH_RAW_SCHEMA)
|
||||
def rc_switch_raw_binary_sensor(var, config):
|
||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||
cg.add(var.set_code(config[CONF_CODE]))
|
||||
|
||||
|
||||
@register_action('rc_switch_raw', RCSwitchRawAction, RC_SWITCH_RAW_SCHEMA)
|
||||
def rc_switch_raw_action(var, config, args):
|
||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||
to_exp=build_rc_switch_protocol)
|
||||
cg.add(var.set_protocol(proto))
|
||||
cg.add(var.set_code((yield cg.templatable(config[CONF_CODE], args, cg.std_string))))
|
||||
|
||||
|
||||
@register_binary_sensor('rc_switch_type_a', RCSwitchRawReceiver, RC_SWITCH_TYPE_A_SCHEMA)
|
||||
def rc_switch_type_a_binary_sensor(var, config):
|
||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||
cg.add(var.set_type_a(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
||||
|
||||
|
||||
@register_action('rc_switch_type_a', RCSwitchTypeAAction, RC_SWITCH_TYPE_A_SCHEMA)
|
||||
def rc_switch_type_a_action(var, config, args):
|
||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||
to_exp=build_rc_switch_protocol)
|
||||
cg.add(var.set_protocol(proto))
|
||||
cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string))))
|
||||
cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.std_string))))
|
||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
||||
|
||||
|
||||
@register_binary_sensor('rc_switch_type_b', RCSwitchRawReceiver, RC_SWITCH_TYPE_B_SCHEMA)
|
||||
def rc_switch_type_b_binary_sensor(var, config):
|
||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||
cg.add(var.set_type_b(config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]))
|
||||
|
||||
|
||||
@register_action('rc_switch_type_b', RCSwitchTypeBAction, RC_SWITCH_TYPE_B_SCHEMA)
|
||||
def rc_switch_type_b_action(var, config, args):
|
||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||
to_exp=build_rc_switch_protocol)
|
||||
cg.add(var.set_protocol(proto))
|
||||
cg.add(var.set_address((yield cg.templatable(config[CONF_ADDRESS], args, cg.uint8))))
|
||||
cg.add(var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8))))
|
||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
||||
|
||||
|
||||
@register_binary_sensor('rc_switch_type_c', RCSwitchRawReceiver, RC_SWITCH_TYPE_C_SCHEMA)
|
||||
def rc_switch_type_c_binary_sensor(var, config):
|
||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||
cg.add(var.set_type_c(config[CONF_FAMILY], config[CONF_GROUP], config[CONF_DEVICE],
|
||||
config[CONF_STATE]))
|
||||
|
||||
|
||||
@register_action('rc_switch_type_c', RCSwitchTypeCAction, RC_SWITCH_TYPE_C_SCHEMA)
|
||||
def rc_switch_type_c_action(var, config, args):
|
||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||
to_exp=build_rc_switch_protocol)
|
||||
cg.add(var.set_protocol(proto))
|
||||
cg.add(var.set_family((yield cg.templatable(config[CONF_FAMILY], args, cg.std_string))))
|
||||
cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.uint8))))
|
||||
cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint8))))
|
||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
||||
|
||||
|
||||
@register_binary_sensor('rc_switch_type_d', RCSwitchRawReceiver, RC_SWITCH_TYPE_D_SCHEMA)
|
||||
def rc_switch_type_d_binary_sensor(var, config):
|
||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||
cg.add(var.set_type_d(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
||||
|
||||
|
||||
@register_action('rc_switch_type_d', RCSwitchTypeDAction, RC_SWITCH_TYPE_D_SCHEMA)
|
||||
def rc_switch_type_d_action(var, config, args):
|
||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||
to_exp=build_rc_switch_protocol)
|
||||
cg.add(var.set_protocol(proto))
|
||||
cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string))))
|
||||
cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint8))))
|
||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
||||
|
||||
|
||||
@register_dumper('rc_switch', RCSwitchDumper)
|
||||
def rc_switch_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
# Samsung
|
||||
(SamsungData, SamsungProtocol, SamsungBinarySensor, SamsungTrigger, SamsungAction,
|
||||
SamsungDumper) = declare_protocol('Samsung')
|
||||
SAMSUNG_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('samsung', SamsungBinarySensor, SAMSUNG_SCHEMA)
|
||||
def samsung_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
SamsungData,
|
||||
('data', config[CONF_DATA]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('samsung', SamsungTrigger, SamsungData)
|
||||
def samsung_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('samsung', SamsungDumper)
|
||||
def samsung_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('samsung', SamsungAction, SAMSUNG_SCHEMA)
|
||||
def samsung_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint16)
|
||||
cg.add(var.set_data(template_))
|
||||
|
||||
|
||||
# Panasonic
|
||||
(PanasonicData, PanasonicProtocol, PanasonicBinarySensor, PanasonicTrigger, PanasonicAction,
|
||||
PanasonicDumper) = declare_protocol('Panasonic')
|
||||
PANASONIC_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
cv.Required(CONF_COMMAND): cv.hex_uint32_t,
|
||||
})
|
||||
|
||||
|
||||
@register_binary_sensor('panasonic', PanasonicBinarySensor, PANASONIC_SCHEMA)
|
||||
def panasonic_binary_sensor(var, config):
|
||||
cg.add(var.set_data(cg.StructInitializer(
|
||||
PanasonicData,
|
||||
('address', config[CONF_ADDRESS]),
|
||||
('command', config[CONF_COMMAND]),
|
||||
)))
|
||||
|
||||
|
||||
@register_trigger('panasonic', PanasonicTrigger, PanasonicData)
|
||||
def panasonic_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper('panasonic', PanasonicDumper)
|
||||
def panasonic_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action('panasonic', PanasonicAction, PANASONIC_SCHEMA)
|
||||
def panasonic_action(var, config, args):
|
||||
template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16)
|
||||
cg.add(var.set_address(template_))
|
||||
template_ = yield cg.templatable(config[CONF_COMMAND], args, cg.uint32)
|
||||
cg.add(var.set_command(template_))
|
51
esphome/components/remote_base/jvc_protocol.cpp
Normal file
51
esphome/components/remote_base/jvc_protocol.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "jvc_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.jvc";
|
||||
|
||||
static const uint8_t NBITS = 16;
|
||||
static const uint32_t HEADER_HIGH_US = 8400;
|
||||
static const uint32_t HEADER_LOW_US = 4200;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1725;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 525;
|
||||
static const uint32_t BIT_HIGH_US = 525;
|
||||
|
||||
void JVCProtocol::encode(RemoteTransmitData *dst, const JVCData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve(2 + NBITS * 2u);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
|
||||
for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) {
|
||||
if (data.data & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
dst->mark(BIT_HIGH_US);
|
||||
}
|
||||
optional<JVCData> JVCProtocol::decode(RemoteReceiveData src) {
|
||||
JVCData out{.data = 0};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
for (uint8_t i = 0; i < NBITS; i++) {
|
||||
out.data <<= 1UL;
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.data |= 1UL;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.data |= 0UL;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
void JVCProtocol::dump(const JVCData &data) { ESP_LOGD(TAG, "Received JVC: data=0x%04X", data.data); }
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
34
esphome/components/remote_base/jvc_protocol.h
Normal file
34
esphome/components/remote_base/jvc_protocol.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct JVCData {
|
||||
uint32_t data;
|
||||
|
||||
bool operator==(const JVCData &rhs) const { return data == rhs.data; }
|
||||
};
|
||||
|
||||
class JVCProtocol : public RemoteProtocol<JVCData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const JVCData &data) override;
|
||||
optional<JVCData> decode(RemoteReceiveData src) override;
|
||||
void dump(const JVCData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(JVC)
|
||||
|
||||
template<typename... Ts> class JVCAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint32_t, data)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
JVCData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
JVCProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
57
esphome/components/remote_base/lg_protocol.cpp
Normal file
57
esphome/components/remote_base/lg_protocol.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "lg_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.lg";
|
||||
|
||||
static const uint32_t HEADER_HIGH_US = 8000;
|
||||
static const uint32_t HEADER_LOW_US = 4000;
|
||||
static const uint32_t BIT_HIGH_US = 600;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1600;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 550;
|
||||
|
||||
void LGProtocol::encode(RemoteTransmitData *dst, const LGData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve(2 + data.nbits * 2u);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
|
||||
for (uint32_t mask = 1UL << (data.nbits - 1); mask != 0; mask >>= 1) {
|
||||
if (data.data & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
dst->mark(BIT_HIGH_US);
|
||||
}
|
||||
optional<LGData> LGProtocol::decode(RemoteReceiveData src) {
|
||||
LGData out{
|
||||
.data = 0,
|
||||
.nbits = 0,
|
||||
};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
for (out.nbits = 0; out.nbits < 32; out.nbits++) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.data = (out.data << 1) | 1;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.data = (out.data << 1) | 0;
|
||||
} else if (out.nbits == 28) {
|
||||
return out;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
void LGProtocol::dump(const LGData &data) {
|
||||
ESP_LOGD(TAG, "Received LG: data=0x%08X, nbits=%d", data.data, data.nbits);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
38
esphome/components/remote_base/lg_protocol.h
Normal file
38
esphome/components/remote_base/lg_protocol.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct LGData {
|
||||
uint32_t data;
|
||||
uint8_t nbits;
|
||||
|
||||
bool operator==(const LGData &rhs) const { return data == rhs.data && nbits == rhs.nbits; }
|
||||
};
|
||||
|
||||
class LGProtocol : public RemoteProtocol<LGData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const LGData &data) override;
|
||||
optional<LGData> decode(RemoteReceiveData src) override;
|
||||
void dump(const LGData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(LG)
|
||||
|
||||
template<typename... Ts> class LGAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint32_t, data)
|
||||
TEMPLATABLE_VALUE(uint8_t, nbits)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
LGData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
data.nbits = this->nbits_.value(x...);
|
||||
LGProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
72
esphome/components/remote_base/nec_protocol.cpp
Normal file
72
esphome/components/remote_base/nec_protocol.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "nec_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.nec";
|
||||
|
||||
static const uint32_t HEADER_HIGH_US = 9000;
|
||||
static const uint32_t HEADER_LOW_US = 4500;
|
||||
static const uint32_t BIT_HIGH_US = 560;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1690;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 560;
|
||||
|
||||
void NECProtocol::encode(RemoteTransmitData *dst, const NECData &data) {
|
||||
dst->reserve(68);
|
||||
dst->set_carrier_frequency(38000);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
for (uint32_t mask = 1UL << 15; mask; mask >>= 1) {
|
||||
if (data.address & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
for (uint32_t mask = 1UL << 15; mask; mask >>= 1) {
|
||||
if (data.command & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
dst->mark(BIT_HIGH_US);
|
||||
}
|
||||
optional<NECData> NECProtocol::decode(RemoteReceiveData src) {
|
||||
NECData data{
|
||||
.address = 0,
|
||||
.command = 0,
|
||||
};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
for (uint32_t mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
data.address |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
data.address &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
data.command |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
data.command &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
src.expect_mark(BIT_HIGH_US);
|
||||
return data;
|
||||
}
|
||||
void NECProtocol::dump(const NECData &data) {
|
||||
ESP_LOGD(TAG, "Received NEC: address=0x%04X, command=0x%04X", data.address, data.command);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
37
esphome/components/remote_base/nec_protocol.h
Normal file
37
esphome/components/remote_base/nec_protocol.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct NECData {
|
||||
uint16_t address;
|
||||
uint16_t command;
|
||||
|
||||
bool operator==(const NECData &rhs) const { return address == rhs.address && command == rhs.command; }
|
||||
};
|
||||
|
||||
class NECProtocol : public RemoteProtocol<NECData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const NECData &data) override;
|
||||
optional<NECData> decode(RemoteReceiveData src) override;
|
||||
void dump(const NECData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(NEC)
|
||||
|
||||
template<typename... Ts> class NECAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, address)
|
||||
TEMPLATABLE_VALUE(uint16_t, command)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
NECData data{};
|
||||
data.address = this->address_.value(x...);
|
||||
data.command = this->command_.value(x...);
|
||||
NECProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
72
esphome/components/remote_base/panasonic_protocol.cpp
Normal file
72
esphome/components/remote_base/panasonic_protocol.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "panasonic_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.panasonic";
|
||||
|
||||
static const uint32_t HEADER_HIGH_US = 3502;
|
||||
static const uint32_t HEADER_LOW_US = 1750;
|
||||
static const uint32_t BIT_HIGH_US = 502;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 400;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1244;
|
||||
|
||||
void PanasonicProtocol::encode(RemoteTransmitData *dst, const PanasonicData &data) {
|
||||
dst->reserve(100);
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
dst->set_carrier_frequency(35000);
|
||||
|
||||
uint32_t mask;
|
||||
for (mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (data.address & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
for (mask = 1UL << 31; mask != 0; mask >>= 1) {
|
||||
if (data.command & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
dst->mark(BIT_HIGH_US);
|
||||
}
|
||||
optional<PanasonicData> PanasonicProtocol::decode(RemoteReceiveData src) {
|
||||
PanasonicData out{
|
||||
.address = 0,
|
||||
.command = 0,
|
||||
};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
uint32_t mask;
|
||||
for (mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.address |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.address &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
for (mask = 1UL << 31; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.command |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.command &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
void PanasonicProtocol::dump(const PanasonicData &data) {
|
||||
ESP_LOGD(TAG, "Received Panasonic: address=0x%04X, command=0x%08X", data.address, data.command);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
38
esphome/components/remote_base/panasonic_protocol.h
Normal file
38
esphome/components/remote_base/panasonic_protocol.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct PanasonicData {
|
||||
uint16_t address;
|
||||
uint32_t command;
|
||||
|
||||
bool operator==(const PanasonicData &rhs) const { return address == rhs.address && command == rhs.command; }
|
||||
};
|
||||
|
||||
class PanasonicProtocol : public RemoteProtocol<PanasonicData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const PanasonicData &data) override;
|
||||
optional<PanasonicData> decode(RemoteReceiveData src) override;
|
||||
void dump(const PanasonicData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Panasonic)
|
||||
|
||||
template<typename... Ts> class PanasonicAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, address)
|
||||
TEMPLATABLE_VALUE(uint32_t, command)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
PanasonicData data{};
|
||||
data.address = this->address_.value(x...);
|
||||
data.command = this->command_.value(x...);
|
||||
PanasonicProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
46
esphome/components/remote_base/raw_protocol.cpp
Normal file
46
esphome/components/remote_base/raw_protocol.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "raw_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.raw";
|
||||
|
||||
void RawDumper::dump(RemoteReceiveData src) {
|
||||
char buffer[256];
|
||||
uint32_t buffer_offset = 0;
|
||||
buffer_offset += sprintf(buffer, "Received Raw: ");
|
||||
|
||||
for (int32_t i = 0; i < src.size(); i++) {
|
||||
const int32_t value = src[i];
|
||||
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
||||
int written;
|
||||
|
||||
if (i + 1 < src.size()) {
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d, ", value);
|
||||
} else {
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d", value);
|
||||
}
|
||||
|
||||
if (written < 0 || written >= int(remaining_length)) {
|
||||
// write failed, flush...
|
||||
buffer[buffer_offset] = '\0';
|
||||
ESP_LOGD(TAG, "%s", buffer);
|
||||
buffer_offset = 0;
|
||||
written = sprintf(buffer, " ");
|
||||
if (i + 1 < src.size()) {
|
||||
written += sprintf(buffer + written, "%d, ", value);
|
||||
} else {
|
||||
written += sprintf(buffer + written, "%d", value);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_offset += written;
|
||||
}
|
||||
if (buffer_offset != 0) {
|
||||
ESP_LOGD(TAG, "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
75
esphome/components/remote_base/raw_protocol.h
Normal file
75
esphome/components/remote_base/raw_protocol.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
class RawBinarySensor : public RemoteReceiverBinarySensorBase {
|
||||
public:
|
||||
bool matches(RemoteReceiveData src) override {
|
||||
for (size_t i = 0; i < this->len_; i++) {
|
||||
auto val = this->data_[i];
|
||||
if (val < 0) {
|
||||
if (!src.expect_space(static_cast<uint32_t>(-val)))
|
||||
return false;
|
||||
} else {
|
||||
if (!src.expect_mark(static_cast<uint32_t>(val)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void set_data(const int32_t *data) { data_ = data; }
|
||||
void set_len(size_t len) { len_ = len; }
|
||||
|
||||
protected:
|
||||
const int32_t *data_;
|
||||
size_t len_;
|
||||
};
|
||||
|
||||
class RawTrigger : public Trigger<std::vector<int32_t>>, public Component, public RemoteReceiverListener {
|
||||
protected:
|
||||
bool on_receive(RemoteReceiveData src) override {
|
||||
this->trigger(*src.get_raw_data());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RawAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
void set_code_template(std::function<std::vector<int32_t>(Ts...)> func) { this->code_func_ = func; }
|
||||
void set_code_static(const int32_t *code, size_t len) {
|
||||
this->code_static_ = code;
|
||||
this->code_static_len_ = len;
|
||||
}
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
// dst->set_data(data);
|
||||
if (this->code_static_ != nullptr) {
|
||||
for (size_t i = 0; i < this->code_static_len_; i++) {
|
||||
auto val = this->code_static_[i];
|
||||
if (val < 0)
|
||||
dst->space(static_cast<uint32_t>(-val));
|
||||
else
|
||||
dst->mark(static_cast<uint32_t>(val));
|
||||
}
|
||||
} else {
|
||||
dst->set_data(this->code_func_(x...));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<std::vector<int32_t>(Ts...)> code_func_{};
|
||||
const int32_t *code_static_{nullptr};
|
||||
int32_t code_static_len_{0};
|
||||
};
|
||||
|
||||
class RawDumper : public RemoteReceiverDumperBase {
|
||||
public:
|
||||
void dump(RemoteReceiveData src) override;
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
62
esphome/components/remote_base/rc5_protocol.cpp
Normal file
62
esphome/components/remote_base/rc5_protocol.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "rc5_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.rc5";
|
||||
|
||||
static const uint32_t BIT_TIME_US = 889;
|
||||
static const uint8_t NBITS = 14;
|
||||
|
||||
void RC5Protocol::encode(RemoteTransmitData *dst, const RC5Data &data) {
|
||||
static bool TOGGLE = false;
|
||||
dst->set_carrier_frequency(36000);
|
||||
|
||||
uint64_t out_data = 0;
|
||||
out_data |= 0b11 << 12;
|
||||
out_data |= TOGGLE << 11;
|
||||
out_data |= data.address << 6;
|
||||
out_data |= data.command;
|
||||
|
||||
for (uint64_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) {
|
||||
if (out_data & mask) {
|
||||
dst->space(BIT_TIME_US);
|
||||
dst->mark(BIT_TIME_US);
|
||||
} else {
|
||||
dst->mark(BIT_TIME_US);
|
||||
dst->space(BIT_TIME_US);
|
||||
}
|
||||
}
|
||||
TOGGLE = !TOGGLE;
|
||||
}
|
||||
optional<RC5Data> RC5Protocol::decode(RemoteReceiveData src) {
|
||||
RC5Data out{
|
||||
.address = 0,
|
||||
.command = 0,
|
||||
};
|
||||
src.expect_space(BIT_TIME_US);
|
||||
if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US) || !src.expect_mark(BIT_TIME_US))
|
||||
return {};
|
||||
|
||||
uint64_t out_data = 0;
|
||||
for (int bit = NBITS - 3; bit >= 0; bit--) {
|
||||
if (src.expect_space(BIT_TIME_US) && src.expect_mark(BIT_TIME_US)) {
|
||||
out_data |= 1 << bit;
|
||||
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(BIT_TIME_US)) {
|
||||
out_data |= 0 << bit;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out.command = out_data & 0x3F;
|
||||
out.address = (out_data >> 6) & 0x1F;
|
||||
return out;
|
||||
}
|
||||
void RC5Protocol::dump(const RC5Data &data) {
|
||||
ESP_LOGD(TAG, "Received RC5: address=0x%02X, command=0x%02X", data.address, data.command);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
38
esphome/components/remote_base/rc5_protocol.h
Normal file
38
esphome/components/remote_base/rc5_protocol.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct RC5Data {
|
||||
uint8_t address;
|
||||
uint8_t command;
|
||||
|
||||
bool operator==(const RC5Data &rhs) const { return address == rhs.address && command == rhs.command; }
|
||||
};
|
||||
|
||||
class RC5Protocol : public RemoteProtocol<RC5Data> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const RC5Data &data) override;
|
||||
optional<RC5Data> decode(RemoteReceiveData src) override;
|
||||
void dump(const RC5Data &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(RC5)
|
||||
|
||||
template<typename... Ts> class RC5Action : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, address)
|
||||
TEMPLATABLE_VALUE(uint8_t, command)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
RC5Data data{};
|
||||
data.address = this->address_.value(x...);
|
||||
data.command = this->command_.value(x...);
|
||||
RC5Protocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
245
esphome/components/remote_base/rc_switch_protocol.cpp
Normal file
245
esphome/components/remote_base/rc_switch_protocol.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "rc_switch_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.rc_switch";
|
||||
|
||||
RCSwitchBase rc_switch_protocols[8] = {RCSwitchBase(0, 0, 0, 0, 0, 0, false),
|
||||
RCSwitchBase(350, 10850, 350, 1050, 1050, 350, false),
|
||||
RCSwitchBase(650, 6500, 650, 1300, 1300, 650, false),
|
||||
RCSwitchBase(3000, 7100, 400, 1100, 900, 600, false),
|
||||
RCSwitchBase(380, 2280, 380, 1140, 1140, 380, false),
|
||||
RCSwitchBase(3000, 7000, 500, 1000, 1000, 500, false),
|
||||
RCSwitchBase(10350, 450, 450, 900, 900, 450, true),
|
||||
RCSwitchBase(300, 9300, 150, 900, 900, 150, false)};
|
||||
|
||||
RCSwitchBase::RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low,
|
||||
uint32_t one_high, uint32_t one_low, bool inverted)
|
||||
: sync_high_(sync_high),
|
||||
sync_low_(sync_low),
|
||||
zero_high_(zero_high),
|
||||
zero_low_(zero_low),
|
||||
one_high_(one_high),
|
||||
one_low_(one_low),
|
||||
inverted_(inverted) {}
|
||||
|
||||
void RCSwitchBase::one(RemoteTransmitData *dst) const {
|
||||
if (!this->inverted_) {
|
||||
dst->mark(this->one_high_);
|
||||
dst->space(this->one_low_);
|
||||
} else {
|
||||
dst->space(this->one_high_);
|
||||
dst->mark(this->one_low_);
|
||||
}
|
||||
}
|
||||
void RCSwitchBase::zero(RemoteTransmitData *dst) const {
|
||||
if (!this->inverted_) {
|
||||
dst->mark(this->zero_high_);
|
||||
dst->space(this->zero_low_);
|
||||
} else {
|
||||
dst->space(this->zero_high_);
|
||||
dst->mark(this->zero_low_);
|
||||
}
|
||||
}
|
||||
void RCSwitchBase::sync(RemoteTransmitData *dst) const {
|
||||
if (!this->inverted_) {
|
||||
dst->mark(this->sync_high_);
|
||||
dst->space(this->sync_low_);
|
||||
} else {
|
||||
dst->space(this->sync_high_);
|
||||
dst->mark(this->sync_low_);
|
||||
}
|
||||
}
|
||||
void RCSwitchBase::transmit(RemoteTransmitData *dst, uint32_t code, uint8_t len) const {
|
||||
for (int16_t i = len - 1; i >= 0; i--) {
|
||||
if (code & (1 << i))
|
||||
this->one(dst);
|
||||
else
|
||||
this->zero(dst);
|
||||
}
|
||||
this->sync(dst);
|
||||
}
|
||||
|
||||
bool RCSwitchBase::expect_one(RemoteReceiveData &src) const {
|
||||
if (!this->inverted_) {
|
||||
if (!src.peek_mark(this->one_high_))
|
||||
return false;
|
||||
if (!src.peek_space(this->one_low_, 1))
|
||||
return false;
|
||||
} else {
|
||||
if (!src.peek_space(this->one_high_))
|
||||
return false;
|
||||
if (!src.peek_mark(this->one_low_, 1))
|
||||
return false;
|
||||
}
|
||||
src.advance(2);
|
||||
return true;
|
||||
}
|
||||
bool RCSwitchBase::expect_zero(RemoteReceiveData &src) const {
|
||||
if (!this->inverted_) {
|
||||
if (!src.peek_mark(this->zero_high_))
|
||||
return false;
|
||||
if (!src.peek_space(this->zero_low_, 1))
|
||||
return false;
|
||||
} else {
|
||||
if (!src.peek_space(this->zero_high_))
|
||||
return false;
|
||||
if (!src.peek_mark(this->zero_low_, 1))
|
||||
return false;
|
||||
}
|
||||
src.advance(2);
|
||||
return true;
|
||||
}
|
||||
bool RCSwitchBase::expect_sync(RemoteReceiveData &src) const {
|
||||
if (!this->inverted_) {
|
||||
if (!src.peek_mark(this->sync_high_))
|
||||
return false;
|
||||
if (!src.peek_space(this->sync_low_, 1))
|
||||
return false;
|
||||
} else {
|
||||
if (!src.peek_space(this->sync_high_))
|
||||
return false;
|
||||
if (!src.peek_mark(this->sync_low_, 1))
|
||||
return false;
|
||||
}
|
||||
src.advance(2);
|
||||
return true;
|
||||
}
|
||||
bool RCSwitchBase::decode(RemoteReceiveData &src, uint32_t *out_data, uint8_t *out_nbits) const {
|
||||
// ignore if sync doesn't exist
|
||||
this->expect_sync(src);
|
||||
|
||||
*out_data = 0;
|
||||
for (*out_nbits = 1; *out_nbits < 32; *out_nbits += 1) {
|
||||
if (this->expect_zero(src)) {
|
||||
*out_data <<= 1;
|
||||
*out_data |= 0;
|
||||
} else if (this->expect_one(src)) {
|
||||
*out_data <<= 1;
|
||||
*out_data |= 1;
|
||||
} else {
|
||||
*out_nbits -= 1;
|
||||
return *out_nbits >= 8;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RCSwitchBase::simple_code_to_tristate(uint16_t code, uint8_t nbits, uint32_t *out_code) {
|
||||
*out_code = 0;
|
||||
for (int8_t i = nbits - 1; i >= 0; i--) {
|
||||
*out_code <<= 2;
|
||||
if (code & (1 << i))
|
||||
*out_code |= 0b01;
|
||||
else
|
||||
*out_code |= 0b00;
|
||||
}
|
||||
}
|
||||
void RCSwitchBase::type_a_code(uint8_t switch_group, uint8_t switch_device, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits) {
|
||||
uint16_t code = 0;
|
||||
code |= (switch_group & 0b0001) ? 0 : 0b1000;
|
||||
code |= (switch_group & 0b0010) ? 0 : 0b0100;
|
||||
code |= (switch_group & 0b0100) ? 0 : 0b0010;
|
||||
code |= (switch_group & 0b1000) ? 0 : 0b0001;
|
||||
code <<= 4;
|
||||
code |= (switch_device & 0b0001) ? 0 : 0b1000;
|
||||
code |= (switch_device & 0b0010) ? 0 : 0b0100;
|
||||
code |= (switch_device & 0b0100) ? 0 : 0b0010;
|
||||
code |= (switch_device & 0b1000) ? 0 : 0b0001;
|
||||
code <<= 2;
|
||||
code |= state ? 0b01 : 0b10;
|
||||
simple_code_to_tristate(code, 10, out_code);
|
||||
*out_nbits = 20;
|
||||
}
|
||||
void RCSwitchBase::type_b_code(uint8_t address_code, uint8_t channel_code, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits) {
|
||||
uint16_t code = 0;
|
||||
code |= (address_code == 1) ? 0 : 0b1000;
|
||||
code |= (address_code == 2) ? 0 : 0b0100;
|
||||
code |= (address_code == 3) ? 0 : 0b0010;
|
||||
code |= (address_code == 4) ? 0 : 0b0001;
|
||||
code <<= 4;
|
||||
code |= (channel_code == 1) ? 0 : 0b1000;
|
||||
code |= (channel_code == 2) ? 0 : 0b0100;
|
||||
code |= (channel_code == 3) ? 0 : 0b0010;
|
||||
code |= (channel_code == 4) ? 0 : 0b0001;
|
||||
code <<= 4;
|
||||
code |= 0b1110;
|
||||
code |= state ? 0b1 : 0b0;
|
||||
simple_code_to_tristate(code, 12, out_code);
|
||||
*out_nbits = 24;
|
||||
}
|
||||
void RCSwitchBase::type_c_code(uint8_t family, uint8_t group, uint8_t device, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits) {
|
||||
uint16_t code = 0;
|
||||
code |= (family & 0b0001) ? 0b1000 : 0;
|
||||
code |= (family & 0b0010) ? 0b0100 : 0;
|
||||
code |= (family & 0b0100) ? 0b0010 : 0;
|
||||
code |= (family & 0b1000) ? 0b0001 : 0;
|
||||
code <<= 4;
|
||||
code |= ((device - 1) & 0b01) ? 0b1000 : 0;
|
||||
code |= ((device - 1) & 0b10) ? 0b0100 : 0;
|
||||
code |= ((group - 1) & 0b01) ? 0b0010 : 0;
|
||||
code |= ((group - 1) & 0b10) ? 0b0001 : 0;
|
||||
code <<= 4;
|
||||
code |= 0b0110;
|
||||
code |= state ? 0b1 : 0b0;
|
||||
simple_code_to_tristate(code, 12, out_code);
|
||||
*out_nbits = 24;
|
||||
}
|
||||
void RCSwitchBase::type_d_code(uint8_t group, uint8_t device, bool state, uint32_t *out_code, uint8_t *out_nbits) {
|
||||
*out_code = 0;
|
||||
*out_code |= (group == 0) ? 0b11000000 : 0b01000000;
|
||||
*out_code |= (group == 1) ? 0b00110000 : 0b00010000;
|
||||
*out_code |= (group == 2) ? 0b00001100 : 0b00000100;
|
||||
*out_code |= (group == 3) ? 0b00000011 : 0b00000001;
|
||||
*out_code <<= 6;
|
||||
*out_code |= (device == 1) ? 0b110000 : 0b010000;
|
||||
*out_code |= (device == 2) ? 0b001100 : 0b000100;
|
||||
*out_code |= (device == 3) ? 0b000011 : 0b000001;
|
||||
*out_code <<= 6;
|
||||
*out_code |= 0b000000;
|
||||
*out_code <<= 4;
|
||||
*out_code |= state ? 0b1100 : 0b0011;
|
||||
*out_nbits = 24;
|
||||
}
|
||||
|
||||
uint32_t decode_binary_string(const std::string &data) {
|
||||
uint32_t ret = 0;
|
||||
for (char c : data) {
|
||||
ret <<= 1UL;
|
||||
ret |= (c != '0');
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool RCSwitchRawReceiver::matches(RemoteReceiveData src) {
|
||||
uint32_t decoded_code;
|
||||
uint8_t decoded_nbits;
|
||||
if (!this->protocol_.decode(src, &decoded_code, &decoded_nbits))
|
||||
return false;
|
||||
|
||||
return decoded_nbits == this->nbits_ && decoded_code == this->code_;
|
||||
}
|
||||
void RCSwitchDumper::dump(RemoteReceiveData src) {
|
||||
for (uint8_t i = 1; i <= 7; i++) {
|
||||
src.reset();
|
||||
uint32_t out_data;
|
||||
uint8_t out_nbits;
|
||||
RCSwitchBase *protocol = &rc_switch_protocols[i];
|
||||
if (protocol->decode(src, &out_data, &out_nbits)) {
|
||||
char buffer[32];
|
||||
for (uint8_t j = 0; j < out_nbits; j++)
|
||||
buffer[j] = (out_data & (1 << (out_nbits - j - 1))) ? '1' : '0';
|
||||
|
||||
buffer[out_nbits] = '\0';
|
||||
ESP_LOGD(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
204
esphome/components/remote_base/rc_switch_protocol.h
Normal file
204
esphome/components/remote_base/rc_switch_protocol.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
class RCSwitchBase {
|
||||
public:
|
||||
RCSwitchBase() = default;
|
||||
RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low, uint32_t one_high,
|
||||
uint32_t one_low, bool inverted);
|
||||
|
||||
void one(RemoteTransmitData *dst) const;
|
||||
|
||||
void zero(RemoteTransmitData *dst) const;
|
||||
|
||||
void sync(RemoteTransmitData *dst) const;
|
||||
|
||||
void transmit(RemoteTransmitData *dst, uint32_t code, uint8_t len) const;
|
||||
|
||||
bool expect_one(RemoteReceiveData &src) const;
|
||||
|
||||
bool expect_zero(RemoteReceiveData &src) const;
|
||||
|
||||
bool expect_sync(RemoteReceiveData &src) const;
|
||||
|
||||
bool decode(RemoteReceiveData &src, uint32_t *out_data, uint8_t *out_nbits) const;
|
||||
|
||||
static void simple_code_to_tristate(uint16_t code, uint8_t nbits, uint32_t *out_code);
|
||||
|
||||
static void type_a_code(uint8_t switch_group, uint8_t switch_device, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits);
|
||||
|
||||
static void type_b_code(uint8_t address_code, uint8_t channel_code, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits);
|
||||
|
||||
static void type_c_code(uint8_t family, uint8_t group, uint8_t device, bool state, uint32_t *out_code,
|
||||
uint8_t *out_nbits);
|
||||
|
||||
static void type_d_code(uint8_t group, uint8_t device, bool state, uint32_t *out_code, uint8_t *out_nbits);
|
||||
|
||||
protected:
|
||||
uint32_t sync_high_{};
|
||||
uint32_t sync_low_{};
|
||||
uint32_t zero_high_{};
|
||||
uint32_t zero_low_{};
|
||||
uint32_t one_high_{};
|
||||
uint32_t one_low_{};
|
||||
bool inverted_{};
|
||||
};
|
||||
|
||||
extern RCSwitchBase rc_switch_protocols[8];
|
||||
|
||||
uint32_t decode_binary_string(const std::string &data);
|
||||
|
||||
template<typename... Ts> class RCSwitchRawAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RCSwitchBase, protocol);
|
||||
TEMPLATABLE_VALUE(std::string, code);
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
auto code = this->code_.value(x...);
|
||||
uint32_t the_code = decode_binary_string(code);
|
||||
uint8_t nbits = code.size();
|
||||
|
||||
auto proto = this->protocol_.value(x...);
|
||||
proto.transmit(dst, the_code, nbits);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RCSwitchTypeAAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RCSwitchBase, protocol);
|
||||
TEMPLATABLE_VALUE(std::string, group);
|
||||
TEMPLATABLE_VALUE(std::string, device);
|
||||
TEMPLATABLE_VALUE(bool, state);
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
auto group = this->group_.value(x...);
|
||||
auto device = this->device_.value(x...);
|
||||
auto state = this->state_.value(x...);
|
||||
uint8_t u_group = decode_binary_string(group);
|
||||
uint8_t u_device = decode_binary_string(device);
|
||||
|
||||
uint32_t code;
|
||||
uint8_t nbits;
|
||||
RCSwitchBase::type_a_code(u_group, u_device, state, &code, &nbits);
|
||||
|
||||
auto proto = this->protocol_.value(x...);
|
||||
proto.transmit(dst, code, nbits);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RCSwitchTypeBAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RCSwitchBase, protocol);
|
||||
TEMPLATABLE_VALUE(uint8_t, address);
|
||||
TEMPLATABLE_VALUE(uint8_t, channel);
|
||||
TEMPLATABLE_VALUE(bool, state);
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
auto address = this->address_.value(x...);
|
||||
auto channel = this->channel_.value(x...);
|
||||
auto state = this->state_.value(x...);
|
||||
|
||||
uint32_t code;
|
||||
uint8_t nbits;
|
||||
RCSwitchBase::type_b_code(address, channel, state, &code, &nbits);
|
||||
|
||||
auto proto = this->protocol_.value(x...);
|
||||
proto.transmit(dst, code, nbits);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RCSwitchTypeCAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RCSwitchBase, protocol);
|
||||
TEMPLATABLE_VALUE(std::string, family);
|
||||
TEMPLATABLE_VALUE(uint8_t, group);
|
||||
TEMPLATABLE_VALUE(uint8_t, device);
|
||||
TEMPLATABLE_VALUE(bool, state);
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
auto family = this->family_.value(x...);
|
||||
auto group = this->group_.value(x...);
|
||||
auto device = this->device_.value(x...);
|
||||
auto state = this->state_.value(x...);
|
||||
|
||||
auto u_family = static_cast<uint8_t>(tolower(family[0]) - 'a');
|
||||
|
||||
uint32_t code;
|
||||
uint8_t nbits;
|
||||
RCSwitchBase::type_c_code(u_family, group, device, state, &code, &nbits);
|
||||
|
||||
auto proto = this->protocol_.value(x...);
|
||||
proto.transmit(dst, code, nbits);
|
||||
}
|
||||
};
|
||||
template<typename... Ts> class RCSwitchTypeDAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RCSwitchBase, protocol);
|
||||
TEMPLATABLE_VALUE(std::string, group);
|
||||
TEMPLATABLE_VALUE(uint8_t, device);
|
||||
TEMPLATABLE_VALUE(bool, state);
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
auto group = this->group_.value(x...);
|
||||
auto device = this->device_.value(x...);
|
||||
auto state = this->state_.value(x...);
|
||||
|
||||
auto u_group = static_cast<uint8_t>(tolower(group[0]) - 'a');
|
||||
|
||||
uint32_t code;
|
||||
uint8_t nbits;
|
||||
RCSwitchBase::type_d_code(u_group, device, state, &code, &nbits);
|
||||
|
||||
auto proto = this->protocol_.value(x...);
|
||||
proto.transmit(dst, code, nbits);
|
||||
}
|
||||
};
|
||||
|
||||
class RCSwitchRawReceiver : public RemoteReceiverBinarySensorBase {
|
||||
public:
|
||||
void set_protocol(const RCSwitchBase &a_protocol) { this->protocol_ = a_protocol; }
|
||||
void set_code(uint32_t code) { this->code_ = code; }
|
||||
void set_code(const std::string &code) {
|
||||
this->code_ = decode_binary_string(code);
|
||||
this->nbits_ = code.size();
|
||||
}
|
||||
void set_nbits(uint8_t nbits) { this->nbits_ = nbits; }
|
||||
void set_type_a(const std::string &group, const std::string &device, bool state) {
|
||||
uint8_t u_group = decode_binary_string(group);
|
||||
uint8_t u_device = decode_binary_string(device);
|
||||
RCSwitchBase::type_a_code(u_group, u_device, state, &this->code_, &this->nbits_);
|
||||
}
|
||||
void set_type_b(uint8_t address_code, uint8_t channel_code, bool state) {
|
||||
RCSwitchBase::type_b_code(address_code, channel_code, state, &this->code_, &this->nbits_);
|
||||
}
|
||||
void set_type_c(std::string family, uint8_t group, uint8_t device, bool state) {
|
||||
auto u_family = static_cast<uint8_t>(tolower(family[0]) - 'a');
|
||||
RCSwitchBase::type_c_code(u_family, group, device, state, &this->code_, &this->nbits_);
|
||||
}
|
||||
void set_type_d(std::string group, uint8_t device, bool state) {
|
||||
auto u_group = static_cast<uint8_t>(tolower(group[0]) - 'a');
|
||||
RCSwitchBase::type_d_code(u_group, device, state, &this->code_, &this->nbits_);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool matches(RemoteReceiveData src) override;
|
||||
|
||||
RCSwitchBase protocol_;
|
||||
uint32_t code_;
|
||||
uint8_t nbits_;
|
||||
};
|
||||
|
||||
class RCSwitchDumper : public RemoteReceiverDumperBase {
|
||||
public:
|
||||
void dump(RemoteReceiveData src) override;
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
59
esphome/components/remote_base/remote_base.cpp
Normal file
59
esphome/components/remote_base/remote_base.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "remote_base.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote_base";
|
||||
|
||||
RemoteComponentBase::RemoteComponentBase(GPIOPin *pin) : pin_(pin) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0;
|
||||
this->channel_ = next_rmt_channel;
|
||||
next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RemoteReceiverBinarySensorBase::dump_config() { LOG_BINARY_SENSOR("", "Remote Receiver Binary Sensor", this); }
|
||||
|
||||
void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
const std::vector<int32_t> &vec = this->temp_.get_data();
|
||||
char buffer[256];
|
||||
uint32_t buffer_offset = 0;
|
||||
buffer_offset += sprintf(buffer, "Sending times=%u wait=%ums: ", send_times, send_wait);
|
||||
|
||||
for (int32_t i = 0; i < vec.size(); i++) {
|
||||
const int32_t value = vec[i];
|
||||
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
||||
int written;
|
||||
|
||||
if (i + 1 < vec.size()) {
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d, ", value);
|
||||
} else {
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d", value);
|
||||
}
|
||||
|
||||
if (written < 0 || written >= int(remaining_length)) {
|
||||
// write failed, flush...
|
||||
buffer[buffer_offset] = '\0';
|
||||
ESP_LOGVV(TAG, "%s", buffer);
|
||||
buffer_offset = 0;
|
||||
written = sprintf(buffer, " ");
|
||||
if (i + 1 < vec.size()) {
|
||||
written += sprintf(buffer + written, "%d, ", value);
|
||||
} else {
|
||||
written += sprintf(buffer + written, "%d", value);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_offset += written;
|
||||
}
|
||||
if (buffer_offset != 0) {
|
||||
ESP_LOGVV(TAG, "%s", buffer);
|
||||
}
|
||||
#endif
|
||||
this->send_internal(send_times, send_wait);
|
||||
}
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
343
esphome/components/remote_base/remote_base.h
Normal file
343
esphome/components/remote_base/remote_base.h
Normal file
@@ -0,0 +1,343 @@
|
||||
#include <utility>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <driver/rmt.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
class RemoteTransmitData {
|
||||
public:
|
||||
void mark(uint32_t length) { this->data_.push_back(length); }
|
||||
|
||||
void space(uint32_t length) { this->data_.push_back(-length); }
|
||||
|
||||
void item(uint32_t mark, uint32_t space) {
|
||||
this->mark(mark);
|
||||
this->space(space);
|
||||
}
|
||||
|
||||
void reserve(uint32_t len) { this->data_.reserve(len); }
|
||||
|
||||
void set_carrier_frequency(uint32_t carrier_frequency) { this->carrier_frequency_ = carrier_frequency; }
|
||||
|
||||
uint32_t get_carrier_frequency() const { return this->carrier_frequency_; }
|
||||
|
||||
const std::vector<int32_t> &get_data() const { return this->data_; }
|
||||
|
||||
void set_data(std::vector<int32_t> data) {
|
||||
this->data_.clear();
|
||||
this->data_.reserve(data.size());
|
||||
for (auto dat : data)
|
||||
this->data_.push_back(dat);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->data_.clear();
|
||||
this->carrier_frequency_ = 0;
|
||||
}
|
||||
|
||||
std::vector<int32_t>::iterator begin() { return this->data_.begin(); }
|
||||
|
||||
std::vector<int32_t>::iterator end() { return this->data_.end(); }
|
||||
|
||||
protected:
|
||||
std::vector<int32_t> data_{};
|
||||
uint32_t carrier_frequency_{0};
|
||||
};
|
||||
|
||||
class RemoteReceiveData {
|
||||
public:
|
||||
RemoteReceiveData(std::vector<int32_t> *data, uint8_t tolerance) : data_(data), tolerance_(tolerance) {}
|
||||
|
||||
bool peek_mark(uint32_t length, uint32_t offset = 0) {
|
||||
if (int32_t(this->index_ + offset) >= this->size())
|
||||
return false;
|
||||
int32_t value = this->peek(offset);
|
||||
const int32_t lo = this->lower_bound_(length);
|
||||
const int32_t hi = this->upper_bound_(length);
|
||||
return value >= 0 && lo <= value && value <= hi;
|
||||
}
|
||||
|
||||
bool peek_space(uint32_t length, uint32_t offset = 0) {
|
||||
if (int32_t(this->index_ + offset) >= this->size())
|
||||
return false;
|
||||
int32_t value = this->peek(offset);
|
||||
const int32_t lo = this->lower_bound_(length);
|
||||
const int32_t hi = this->upper_bound_(length);
|
||||
return value <= 0 && lo <= -value && -value <= hi;
|
||||
}
|
||||
|
||||
bool peek_space_at_least(uint32_t length, uint32_t offset = 0) {
|
||||
if (int32_t(this->index_ + offset) >= this->size())
|
||||
return false;
|
||||
int32_t value = this->pos(this->index_ + offset);
|
||||
const int32_t lo = this->lower_bound_(length);
|
||||
return value <= 0 && lo <= -value;
|
||||
}
|
||||
|
||||
bool peek_item(uint32_t mark, uint32_t space, uint32_t offset = 0) {
|
||||
return this->peek_mark(mark, offset) && this->peek_space(space, offset + 1);
|
||||
}
|
||||
|
||||
int32_t peek(uint32_t offset = 0) { return (*this)[this->index_ + offset]; }
|
||||
|
||||
void advance(uint32_t amount = 1) { this->index_ += amount; }
|
||||
|
||||
bool expect_mark(uint32_t length) {
|
||||
if (this->peek_mark(length)) {
|
||||
this->advance();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expect_space(uint32_t length) {
|
||||
if (this->peek_space(length)) {
|
||||
this->advance();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expect_item(uint32_t mark, uint32_t space) {
|
||||
if (this->peek_item(mark, space)) {
|
||||
this->advance(2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset() { this->index_ = 0; }
|
||||
|
||||
int32_t pos(uint32_t index) const { return (*this->data_)[index]; }
|
||||
|
||||
int32_t operator[](uint32_t index) const { return this->pos(index); }
|
||||
|
||||
int32_t size() const { return this->data_->size(); }
|
||||
|
||||
std::vector<int32_t> *get_raw_data() { return this->data_; }
|
||||
|
||||
protected:
|
||||
int32_t lower_bound_(uint32_t length) { return int32_t(100 - this->tolerance_) * length / 100U; }
|
||||
int32_t upper_bound_(uint32_t length) { return int32_t(100 + this->tolerance_) * length / 100U; }
|
||||
|
||||
uint32_t index_{0};
|
||||
std::vector<int32_t> *data_;
|
||||
uint8_t tolerance_;
|
||||
};
|
||||
|
||||
template<typename T> class RemoteProtocol {
|
||||
public:
|
||||
virtual void encode(RemoteTransmitData *dst, const T &data) = 0;
|
||||
|
||||
virtual optional<T> decode(RemoteReceiveData src) = 0;
|
||||
|
||||
virtual void dump(const T &data) = 0;
|
||||
};
|
||||
|
||||
class RemoteComponentBase {
|
||||
public:
|
||||
explicit RemoteComponentBase(GPIOPin *pin);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void set_channel(rmt_channel_t channel) { this->channel_ = channel; }
|
||||
void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint32_t from_microseconds(uint32_t us) {
|
||||
const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u;
|
||||
return us * ticks_per_ten_us / 10;
|
||||
}
|
||||
uint32_t to_microseconds(uint32_t ticks) {
|
||||
const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u;
|
||||
return (ticks * 10) / ticks_per_ten_us;
|
||||
}
|
||||
#endif
|
||||
|
||||
GPIOPin *pin_;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
rmt_channel_t channel_{RMT_CHANNEL_0};
|
||||
uint8_t clock_divider_{80};
|
||||
esp_err_t error_code_{ESP_OK};
|
||||
#endif
|
||||
};
|
||||
|
||||
class RemoteTransmitterBase : public RemoteComponentBase {
|
||||
public:
|
||||
RemoteTransmitterBase(GPIOPin *pin) : RemoteComponentBase(pin) {}
|
||||
class TransmitCall {
|
||||
public:
|
||||
explicit TransmitCall(RemoteTransmitterBase *parent) : parent_(parent) {}
|
||||
RemoteTransmitData *get_data() { return &this->parent_->temp_; }
|
||||
void set_send_times(uint32_t send_times) { send_times_ = send_times; }
|
||||
void set_send_wait(uint32_t send_wait) { send_wait_ = send_wait; }
|
||||
|
||||
void perform() { this->parent_->send_(this->send_times_, this->send_wait_); }
|
||||
|
||||
protected:
|
||||
RemoteTransmitterBase *parent_;
|
||||
uint32_t send_times_{1};
|
||||
uint32_t send_wait_{0};
|
||||
};
|
||||
|
||||
TransmitCall transmit() {
|
||||
this->temp_.reset();
|
||||
return TransmitCall(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
void send_(uint32_t send_times, uint32_t send_wait);
|
||||
virtual void send_internal(uint32_t send_times, uint32_t send_wait) = 0;
|
||||
void send_single_() { this->send_(1, 0); }
|
||||
|
||||
/// Use same vector for all transmits, avoids many allocations
|
||||
RemoteTransmitData temp_;
|
||||
};
|
||||
|
||||
class RemoteReceiverListener {
|
||||
public:
|
||||
virtual bool on_receive(RemoteReceiveData data) = 0;
|
||||
};
|
||||
|
||||
class RemoteReceiverDumperBase {
|
||||
public:
|
||||
virtual void dump(RemoteReceiveData src) = 0;
|
||||
};
|
||||
|
||||
class RemoteReceiverBase : public RemoteComponentBase {
|
||||
public:
|
||||
RemoteReceiverBase(GPIOPin *pin) : RemoteComponentBase(pin) {}
|
||||
void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); }
|
||||
void register_dumper(RemoteReceiverDumperBase *dumper) { this->dumpers_.push_back(dumper); }
|
||||
void set_tolerance(uint8_t tolerance) { tolerance_ = tolerance; }
|
||||
|
||||
protected:
|
||||
bool call_listeners_() {
|
||||
bool success = false;
|
||||
for (auto *listener : this->listeners_) {
|
||||
auto data = RemoteReceiveData(&this->temp_, this->tolerance_);
|
||||
if (listener->on_receive(data))
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
void call_dumpers_() {
|
||||
for (auto *dumper : this->dumpers_) {
|
||||
auto data = RemoteReceiveData(&this->temp_, this->tolerance_);
|
||||
dumper->dump(data);
|
||||
}
|
||||
}
|
||||
void call_listeners_dumpers_() {
|
||||
if (this->call_listeners_())
|
||||
return;
|
||||
// If a listener handled, then do not dump
|
||||
this->call_dumpers_();
|
||||
}
|
||||
|
||||
std::vector<RemoteReceiverListener *> listeners_;
|
||||
std::vector<RemoteReceiverDumperBase *> dumpers_;
|
||||
std::vector<int32_t> temp_;
|
||||
uint8_t tolerance_{25};
|
||||
};
|
||||
|
||||
class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensor,
|
||||
public Component,
|
||||
public RemoteReceiverListener {
|
||||
public:
|
||||
explicit RemoteReceiverBinarySensorBase() : BinarySensor() {}
|
||||
void dump_config() override;
|
||||
virtual bool matches(RemoteReceiveData src) = 0;
|
||||
bool on_receive(RemoteReceiveData src) override {
|
||||
if (this->matches(src)) {
|
||||
this->publish_state(true);
|
||||
yield();
|
||||
this->publish_state(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename D> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
|
||||
public:
|
||||
RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {}
|
||||
|
||||
protected:
|
||||
bool matches(RemoteReceiveData src) override {
|
||||
auto proto = T();
|
||||
auto res = proto.decode(src);
|
||||
return res.has_value();
|
||||
}
|
||||
|
||||
public:
|
||||
void set_data(D data) { data_ = data; }
|
||||
|
||||
protected:
|
||||
D data_;
|
||||
};
|
||||
|
||||
template<typename T, typename D> class RemoteReceiverTrigger : public Trigger<D>, public RemoteReceiverListener {
|
||||
protected:
|
||||
bool on_receive(RemoteReceiveData src) override {
|
||||
auto proto = T();
|
||||
auto res = proto.decode(src);
|
||||
if (res.has_value()) {
|
||||
this->trigger(*res);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RemoteTransmitterActionBase : public Action<Ts...> {
|
||||
public:
|
||||
void set_parent(RemoteTransmitterBase *parent) { this->parent_ = parent; }
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto call = this->parent_->transmit();
|
||||
this->encode(call.get_data(), x...);
|
||||
call.set_send_times(this->send_times_.value_or(x..., 1));
|
||||
call.set_send_wait(this->send_wait_.value_or(x..., 0));
|
||||
call.perform();
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
virtual void encode(RemoteTransmitData *dst, Ts... x) = 0;
|
||||
|
||||
TEMPLATABLE_VALUE(uint32_t, send_times);
|
||||
TEMPLATABLE_VALUE(uint32_t, send_wait);
|
||||
|
||||
protected:
|
||||
RemoteTransmitterBase *parent_{};
|
||||
};
|
||||
|
||||
template<typename T, typename D> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
|
||||
public:
|
||||
void dump(RemoteReceiveData src) override {
|
||||
auto proto = T();
|
||||
auto decoded = proto.decode(src);
|
||||
if (!decoded.has_value())
|
||||
return;
|
||||
proto.dump(*decoded);
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_REMOTE_PROTOCOL_(prefix) \
|
||||
using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol, prefix##Data>; \
|
||||
using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol, prefix##Data>; \
|
||||
using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol, prefix##Data>;
|
||||
#define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix)
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
58
esphome/components/remote_base/samsung_protocol.cpp
Normal file
58
esphome/components/remote_base/samsung_protocol.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "samsung_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.samsung";
|
||||
|
||||
static const uint8_t NBITS = 32;
|
||||
static const uint32_t HEADER_HIGH_US = 4500;
|
||||
static const uint32_t HEADER_LOW_US = 4500;
|
||||
static const uint32_t BIT_HIGH_US = 560;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1690;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 560;
|
||||
static const uint32_t FOOTER_HIGH_US = 560;
|
||||
static const uint32_t FOOTER_LOW_US = 560;
|
||||
|
||||
void SamsungProtocol::encode(RemoteTransmitData *dst, const SamsungData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve(4 + NBITS * 2u);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
|
||||
for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) {
|
||||
if (data.data & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
dst->item(FOOTER_HIGH_US, FOOTER_LOW_US);
|
||||
}
|
||||
optional<SamsungData> SamsungProtocol::decode(RemoteReceiveData src) {
|
||||
SamsungData out{
|
||||
.data = 0,
|
||||
};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
for (uint8_t i = 0; i < NBITS; i++) {
|
||||
out.data <<= 1UL;
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.data |= 1UL;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.data |= 0UL;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!src.expect_mark(FOOTER_HIGH_US))
|
||||
return {};
|
||||
return out;
|
||||
}
|
||||
void SamsungProtocol::dump(const SamsungData &data) { ESP_LOGD(TAG, "Received Samsung: data=0x%08X", data.data); }
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
35
esphome/components/remote_base/samsung_protocol.h
Normal file
35
esphome/components/remote_base/samsung_protocol.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct SamsungData {
|
||||
uint32_t data;
|
||||
|
||||
bool operator==(const SamsungData &rhs) const { return data == rhs.data; }
|
||||
};
|
||||
|
||||
class SamsungProtocol : public RemoteProtocol<SamsungData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const SamsungData &data) override;
|
||||
optional<SamsungData> decode(RemoteReceiveData src) override;
|
||||
void dump(const SamsungData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Samsung)
|
||||
|
||||
template<typename... Ts> class SamsungAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint32_t, data)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
SamsungData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
SamsungProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
68
esphome/components/remote_base/sony_protocol.cpp
Normal file
68
esphome/components/remote_base/sony_protocol.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "sony_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *TAG = "remote.sony";
|
||||
|
||||
static const uint32_t HEADER_HIGH_US = 2400;
|
||||
static const uint32_t HEADER_LOW_US = 600;
|
||||
static const uint32_t BIT_ONE_HIGH_US = 1200;
|
||||
static const uint32_t BIT_ZERO_HIGH_US = 600;
|
||||
static const uint32_t BIT_LOW_US = 600;
|
||||
|
||||
void SonyProtocol::encode(RemoteTransmitData *dst, const SonyData &data) {
|
||||
dst->set_carrier_frequency(40000);
|
||||
dst->reserve(2 + data.nbits * 2u);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
|
||||
for (uint32_t mask = 1UL << (data.nbits - 1); mask != 0; mask >>= 1) {
|
||||
if (data.data & mask)
|
||||
dst->item(BIT_ONE_HIGH_US, BIT_LOW_US);
|
||||
else
|
||||
dst->item(BIT_ZERO_HIGH_US, BIT_LOW_US);
|
||||
}
|
||||
}
|
||||
optional<SonyData> SonyProtocol::decode(RemoteReceiveData src) {
|
||||
SonyData out{
|
||||
.data = 0,
|
||||
.nbits = 0,
|
||||
};
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return out;
|
||||
|
||||
for (; out.nbits < 20; out.nbits++) {
|
||||
uint32_t bit;
|
||||
if (src.expect_mark(BIT_ONE_HIGH_US)) {
|
||||
bit = 1;
|
||||
} else if (src.expect_mark(BIT_ZERO_HIGH_US)) {
|
||||
bit = 0;
|
||||
} else if (out.nbits == 12 || out.nbits == 15) {
|
||||
return out;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
out.data = (out.data << 1UL) | bit;
|
||||
if (src.expect_space(BIT_LOW_US)) {
|
||||
// nothing needs to be done
|
||||
} else if (src.peek_space_at_least(BIT_LOW_US)) {
|
||||
out.nbits += 1;
|
||||
if (out.nbits == 12 || out.nbits == 15 || out.nbits == 20)
|
||||
return out;
|
||||
return {};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
void SonyProtocol::dump(const SonyData &data) {
|
||||
ESP_LOGD(TAG, "Received Sony: data=0x%08X, nbits=%d", data.data, data.nbits);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
38
esphome/components/remote_base/sony_protocol.h
Normal file
38
esphome/components/remote_base/sony_protocol.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct SonyData {
|
||||
uint32_t data;
|
||||
uint8_t nbits;
|
||||
|
||||
bool operator==(const SonyData &rhs) const { return data == rhs.data && nbits == rhs.nbits; }
|
||||
};
|
||||
|
||||
class SonyProtocol : public RemoteProtocol<SonyData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const SonyData &data) override;
|
||||
optional<SonyData> decode(RemoteReceiveData src) override;
|
||||
void dump(const SonyData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Sony)
|
||||
|
||||
template<typename... Ts> class SonyAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint32_t, data)
|
||||
TEMPLATABLE_VALUE(uint8_t, nbits)
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
SonyData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
data.nbits = this->nbits_.value(x...);
|
||||
SonyProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
Reference in New Issue
Block a user