mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +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_RC_CODE_2, | ||||||
|     CONF_REPEAT, |     CONF_REPEAT, | ||||||
|     CONF_SECOND, |     CONF_SECOND, | ||||||
|  |     CONF_SOURCE, | ||||||
|     CONF_STATE, |     CONF_STATE, | ||||||
|     CONF_SYNC, |     CONF_SYNC, | ||||||
|     CONF_TIMES, |     CONF_TIMES, | ||||||
| @@ -265,6 +266,53 @@ async def build_dumpers(config): | |||||||
|     return dumpers |     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 | # ByronSX | ||||||
| ( | ( | ||||||
|     ByronSXData, |     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: |     - logger.log: | ||||||
|         format: "on_abbwelcome: %u" |         format: "on_abbwelcome: %u" | ||||||
|         args: ["x.data()[0]"] |         args: ["x.data()[0]"] | ||||||
|  | on_beo4: | ||||||
|  |   then: | ||||||
|  |     - logger.log: | ||||||
|  |         format: "on_beo4: %u %u" | ||||||
|  |         args: ["x.source", "x.command"] | ||||||
| on_aeha: | on_aeha: | ||||||
|   then: |   then: | ||||||
|     - logger.log: |     - logger.log: | ||||||
|   | |||||||
| @@ -1,4 +1,11 @@ | |||||||
| button: | button: | ||||||
|  |   - platform: template | ||||||
|  |     name: Beo4 audio mute | ||||||
|  |     id: beo4_audio_mute | ||||||
|  |     on_press: | ||||||
|  |       remote_transmitter.transmit_beo4: | ||||||
|  |         source: 0x01 | ||||||
|  |         command: 0x0C | ||||||
|   - platform: template |   - platform: template | ||||||
|     name: JVC Off |     name: JVC Off | ||||||
|     id: living_room_lights_on |     id: living_room_lights_on | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user