mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	add beo4_protocol to remote_base component (#8307)
This commit is contained in:
		| @@ -28,6 +28,7 @@ from esphome.const import ( | ||||
|     CONF_RC_CODE_2, | ||||
|     CONF_REPEAT, | ||||
|     CONF_SECOND, | ||||
|     CONF_SOURCE, | ||||
|     CONF_STATE, | ||||
|     CONF_SYNC, | ||||
|     CONF_TIMES, | ||||
| @@ -265,6 +266,53 @@ async def build_dumpers(config): | ||||
|     return dumpers | ||||
|  | ||||
|  | ||||
| # Beo4 | ||||
| Beo4Data, Beo4BinarySensor, Beo4Trigger, Beo4Action, Beo4Dumper = declare_protocol( | ||||
|     "Beo4" | ||||
| ) | ||||
| BEO4_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_SOURCE): cv.hex_uint8_t, | ||||
|         cv.Required(CONF_COMMAND): cv.hex_uint8_t, | ||||
|         cv.Optional(CONF_COMMAND_REPEATS, default=1): cv.uint8_t, | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @register_binary_sensor("beo4", Beo4BinarySensor, BEO4_SCHEMA) | ||||
| def beo4_binary_sensor(var, config): | ||||
|     cg.add( | ||||
|         var.set_data( | ||||
|             cg.StructInitializer( | ||||
|                 Beo4Data, | ||||
|                 ("source", config[CONF_SOURCE]), | ||||
|                 ("command", config[CONF_COMMAND]), | ||||
|                 ("repeats", config[CONF_COMMAND_REPEATS]), | ||||
|             ) | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @register_trigger("beo4", Beo4Trigger, Beo4Data) | ||||
| def beo4_trigger(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_dumper("beo4", Beo4Dumper) | ||||
| def beo4_dumper(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_action("beo4", Beo4Action, BEO4_SCHEMA) | ||||
| async def beo4_action(var, config, args): | ||||
|     template_ = await cg.templatable(config[CONF_SOURCE], args, cg.uint8) | ||||
|     cg.add(var.set_source(template_)) | ||||
|     template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) | ||||
|     cg.add(var.set_command(template_)) | ||||
|     template_ = await cg.templatable(config[CONF_COMMAND_REPEATS], args, cg.uint8) | ||||
|     cg.add(var.set_repeats(template_)) | ||||
|  | ||||
|  | ||||
| # ByronSX | ||||
| ( | ||||
|     ByronSXData, | ||||
|   | ||||
							
								
								
									
										151
									
								
								esphome/components/remote_base/beo4_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								esphome/components/remote_base/beo4_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| #include "beo4_protocol.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| static const char *const TAG = "remote.beo4"; | ||||
|  | ||||
| // beo4 pulse width, high=carrier_pulse low=data_pulse | ||||
| constexpr uint16_t PW_CARR_US = 200;     // carrier pulse length | ||||
| constexpr uint16_t PW_ZERO_US = 2925;    // + 200 =  3125 µs | ||||
| constexpr uint16_t PW_SAME_US = 6050;    // + 200 =  6250 µs | ||||
| constexpr uint16_t PW_ONE_US = 9175;     // + 200 =  9375 µs | ||||
| constexpr uint16_t PW_STOP_US = 12300;   // + 200 = 12500 µs | ||||
| constexpr uint16_t PW_START_US = 15425;  // + 200 = 15625 µs | ||||
|  | ||||
| // beo4 pulse codes | ||||
| constexpr uint8_t PC_ZERO = (PW_CARR_US + PW_ZERO_US) / 3125;    // =1 | ||||
| constexpr uint8_t PC_SAME = (PW_CARR_US + PW_SAME_US) / 3125;    // =2 | ||||
| constexpr uint8_t PC_ONE = (PW_CARR_US + PW_ONE_US) / 3125;      // =3 | ||||
| constexpr uint8_t PC_STOP = (PW_CARR_US + PW_STOP_US) / 3125;    // =4 | ||||
| constexpr uint8_t PC_START = (PW_CARR_US + PW_START_US) / 3125;  // =5 | ||||
|  | ||||
| // beo4 number of data bits = beoLink+beoSrc+beoCmd = 1+8+8 = 17 | ||||
| constexpr uint32_t N_BITS = 1 + 8 + 8; | ||||
|  | ||||
| // required symbols = 2*(start_sequence + n_bits + stop) = 2*(3+17+1) = 42 | ||||
| constexpr uint32_t N_SYM = 2 + ((3 + 17 + 1) * 2u);  // + 2 = 44 | ||||
|  | ||||
| // states finite-state-machine decoder | ||||
| enum class RxSt { RX_IDLE, RX_DATA, RX_STOP }; | ||||
|  | ||||
| void Beo4Protocol::encode(RemoteTransmitData *dst, const Beo4Data &data) { | ||||
|   uint32_t beo_code = ((uint32_t) data.source << 8) + (uint32_t) data.command; | ||||
|   uint32_t jc = 0, ic = 0; | ||||
|   uint32_t cur_bit = 0; | ||||
|   uint32_t pre_bit = 0; | ||||
|   dst->set_carrier_frequency(455000); | ||||
|   dst->reserve(N_SYM); | ||||
|  | ||||
|   // start sequence=zero,zero,start | ||||
|   dst->item(PW_CARR_US, PW_ZERO_US); | ||||
|   dst->item(PW_CARR_US, PW_ZERO_US); | ||||
|   dst->item(PW_CARR_US, PW_START_US); | ||||
|  | ||||
|   // the data-bit BeoLink is always 0 | ||||
|   dst->item(PW_CARR_US, PW_ZERO_US); | ||||
|  | ||||
|   // The B&O trick to avoid extra long and extra short | ||||
|   // code-frames by extracting the data-bits from left | ||||
|   // to right, then comparing current with previous bit | ||||
|   // and set pulse to "same" "one" or "zero" | ||||
|   for (jc = 15, ic = 0; ic < 16; ic++, jc--) { | ||||
|     cur_bit = ((beo_code) >> jc) & 1; | ||||
|     if (cur_bit == pre_bit) { | ||||
|       dst->item(PW_CARR_US, PW_SAME_US); | ||||
|     } else if (1 == cur_bit) { | ||||
|       dst->item(PW_CARR_US, PW_ONE_US); | ||||
|     } else { | ||||
|       dst->item(PW_CARR_US, PW_ZERO_US); | ||||
|     } | ||||
|     pre_bit = cur_bit; | ||||
|   } | ||||
|   // complete the frame with stop-symbol and final carrier pulse | ||||
|   dst->item(PW_CARR_US, PW_STOP_US); | ||||
|   dst->mark(PW_CARR_US); | ||||
| } | ||||
|  | ||||
| optional<Beo4Data> Beo4Protocol::decode(RemoteReceiveData src) { | ||||
|   int32_t n_sym = src.size(); | ||||
|   Beo4Data data{ | ||||
|       .source = 0, | ||||
|       .command = 0, | ||||
|       .repeats = 0, | ||||
|   }; | ||||
|   // suppress dummy codes (TSO7000 hiccups) | ||||
|   if (n_sym > 42) { | ||||
|     static uint32_t beo_code = 0; | ||||
|     RxSt fsm = RxSt::RX_IDLE; | ||||
|     int32_t ic = 0; | ||||
|     int32_t jc = 0; | ||||
|     uint32_t pre_bit = 0; | ||||
|     uint32_t cnt_bit = 0; | ||||
|     ESP_LOGD(TAG, "Beo4: n_sym=%d ", n_sym); | ||||
|     for (jc = 0, ic = 0; ic < (n_sym - 1); ic += 2, jc++) { | ||||
|       int32_t pulse_width = src[ic] - src[ic + 1]; | ||||
|       // suppress TSOP7000 (dummy pulses) | ||||
|       if (pulse_width > 1500) { | ||||
|         int32_t pulse_code = (pulse_width + 1560) / 3125; | ||||
|         switch (fsm) { | ||||
|           case RxSt::RX_IDLE: { | ||||
|             beo_code = 0; | ||||
|             cnt_bit = 0; | ||||
|             pre_bit = 0; | ||||
|             if (PC_START == pulse_code) { | ||||
|               fsm = RxSt::RX_DATA; | ||||
|             } | ||||
|             break; | ||||
|           } | ||||
|           case RxSt::RX_DATA: { | ||||
|             uint32_t cur_bit = 0; | ||||
|             switch (pulse_code) { | ||||
|               case PC_ZERO: { | ||||
|                 cur_bit = pre_bit = 0; | ||||
|                 break; | ||||
|               } | ||||
|               case PC_SAME: { | ||||
|                 cur_bit = pre_bit; | ||||
|                 break; | ||||
|               } | ||||
|               case PC_ONE: { | ||||
|                 cur_bit = pre_bit = 1; | ||||
|                 break; | ||||
|               } | ||||
|               default: { | ||||
|                 fsm = RxSt::RX_IDLE; | ||||
|                 break; | ||||
|               } | ||||
|             } | ||||
|             beo_code = (beo_code << 1) + cur_bit; | ||||
|             if (++cnt_bit == N_BITS) { | ||||
|               fsm = RxSt::RX_STOP; | ||||
|             } | ||||
|             break; | ||||
|           } | ||||
|           case RxSt::RX_STOP: { | ||||
|             if (PC_STOP == pulse_code) { | ||||
|               data.source = (uint8_t) ((beo_code >> 8) & 0xff); | ||||
|               data.command = (uint8_t) ((beo_code) &0xff); | ||||
|               data.repeats++; | ||||
|             } | ||||
|             if ((n_sym - ic) < 42) { | ||||
|               return data; | ||||
|             } else { | ||||
|               fsm = RxSt::RX_IDLE; | ||||
|             } | ||||
|             break; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return {};  // decoding failed | ||||
| } | ||||
|  | ||||
| void Beo4Protocol::dump(const Beo4Data &data) { | ||||
|   ESP_LOGI(TAG, "Beo4: source=0x%02x command=0x%02x repeats=%d ", data.source, data.command, data.repeats); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
							
								
								
									
										43
									
								
								esphome/components/remote_base/beo4_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/remote_base/beo4_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "remote_base.h" | ||||
|  | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| struct Beo4Data { | ||||
|   uint8_t source;   // beoSource, e.g. video, audio, light... | ||||
|   uint8_t command;  // beoCommend, e.g. volume+, mute,... | ||||
|   uint8_t repeats;  // beoRepeat for repeat commands, e.g. up, down... | ||||
|  | ||||
|   bool operator==(const Beo4Data &rhs) const { return source == rhs.source && command == rhs.command; } | ||||
| }; | ||||
|  | ||||
| class Beo4Protocol : public RemoteProtocol<Beo4Data> { | ||||
|  public: | ||||
|   void encode(RemoteTransmitData *dst, const Beo4Data &data) override; | ||||
|   optional<Beo4Data> decode(RemoteReceiveData src) override; | ||||
|   void dump(const Beo4Data &data) override; | ||||
| }; | ||||
|  | ||||
| DECLARE_REMOTE_PROTOCOL(Beo4) | ||||
|  | ||||
| template<typename... Ts> class Beo4Action : public RemoteTransmitterActionBase<Ts...> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint8_t, source) | ||||
|   TEMPLATABLE_VALUE(uint8_t, command) | ||||
|   TEMPLATABLE_VALUE(uint8_t, repeats) | ||||
|  | ||||
|   void encode(RemoteTransmitData *dst, Ts... x) override { | ||||
|     Beo4Data data{}; | ||||
|     data.source = this->source_.value(x...); | ||||
|     data.command = this->command_.value(x...); | ||||
|     data.repeats = this->repeats_.value(x...); | ||||
|     Beo4Protocol().encode(dst, data); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
| @@ -3,6 +3,11 @@ on_abbwelcome: | ||||
|     - logger.log: | ||||
|         format: "on_abbwelcome: %u" | ||||
|         args: ["x.data()[0]"] | ||||
| on_beo4: | ||||
|   then: | ||||
|     - logger.log: | ||||
|         format: "on_beo4: %u %u" | ||||
|         args: ["x.source", "x.command"] | ||||
| on_aeha: | ||||
|   then: | ||||
|     - logger.log: | ||||
|   | ||||
| @@ -1,4 +1,11 @@ | ||||
| button: | ||||
|   - platform: template | ||||
|     name: Beo4 audio mute | ||||
|     id: beo4_audio_mute | ||||
|     on_press: | ||||
|       remote_transmitter.transmit_beo4: | ||||
|         source: 0x01 | ||||
|         command: 0x0C | ||||
|   - platform: template | ||||
|     name: JVC Off | ||||
|     id: living_room_lights_on | ||||
|   | ||||
		Reference in New Issue
	
	Block a user