mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add dfplayer mini component (#655)
* Add dfplayer mini component * receiving some data * implemented many actions * lint * undo homeassistant_time.h * Update esphome/components/dfplayer/__init__.py Co-Authored-By: Otto Winter <otto@otto-winter.com> * Update esphome/components/dfplayer/dfplayer.cpp Co-Authored-By: Otto Winter <otto@otto-winter.com> * add set device. fixes * lint * Fixes and sync with docs * add test * lint * lint * lint
This commit is contained in:
		
				
					committed by
					
						 Otto Winter
						Otto Winter
					
				
			
			
				
	
			
			
			
						parent
						
							18426b71e4
						
					
				
				
					commit
					af15a4e710
				
			
							
								
								
									
										221
									
								
								esphome/components/dfplayer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								esphome/components/dfplayer/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.const import CONF_ID, CONF_TRIGGER_ID | ||||
| from esphome.components import uart | ||||
|  | ||||
| DEPENDENCIES = ['uart'] | ||||
|  | ||||
| dfplayer_ns = cg.esphome_ns.namespace('dfplayer') | ||||
| DFPlayer = dfplayer_ns.class_('DFPlayer', cg.Component) | ||||
| DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_('DFPlayerFinishedPlaybackTrigger', | ||||
|                                                      automation.Trigger.template()) | ||||
| DFPlayerIsPlayingCondition = dfplayer_ns.class_('DFPlayerIsPlayingCondition', automation.Condition) | ||||
|  | ||||
| MULTI_CONF = True | ||||
| CONF_FOLDER = 'folder' | ||||
| CONF_FILE = 'file' | ||||
| CONF_LOOP = 'loop' | ||||
| CONF_VOLUME = 'volume' | ||||
| CONF_DEVICE = 'device' | ||||
| CONF_EQ_PRESET = 'eq_preset' | ||||
| CONF_ON_FINISHED_PLAYBACK = 'on_finished_playback' | ||||
|  | ||||
| EqPreset = dfplayer_ns.enum("EqPreset") | ||||
| EQ_PRESET = { | ||||
|     'NORMAL': EqPreset.NORMAL, | ||||
|     'POP': EqPreset.POP, | ||||
|     'ROCK': EqPreset.ROCK, | ||||
|     'JAZZ': EqPreset.JAZZ, | ||||
|     'CLASSIC': EqPreset.CLASSIC, | ||||
|     'BASS': EqPreset.BASS, | ||||
| } | ||||
| Device = dfplayer_ns.enum("Device") | ||||
| DEVICE = { | ||||
|     'USB': Device.USB, | ||||
|     'TF_CARD': Device.TF_CARD, | ||||
| } | ||||
|  | ||||
| NextAction = dfplayer_ns.class_('NextAction', automation.Action) | ||||
| PreviousAction = dfplayer_ns.class_('PreviousAction', automation.Action) | ||||
| PlayFileAction = dfplayer_ns.class_('PlayFileAction', automation.Action) | ||||
| PlayFolderAction = dfplayer_ns.class_('PlayFolderAction', automation.Action) | ||||
| SetVolumeAction = dfplayer_ns.class_('SetVolumeAction', automation.Action) | ||||
| SetEqAction = dfplayer_ns.class_('SetEqAction', automation.Action) | ||||
| SleepAction = dfplayer_ns.class_('SleepAction', automation.Action) | ||||
| ResetAction = dfplayer_ns.class_('ResetAction', automation.Action) | ||||
| StartAction = dfplayer_ns.class_('StartAction', automation.Action) | ||||
| PauseAction = dfplayer_ns.class_('PauseAction', automation.Action) | ||||
| StopAction = dfplayer_ns.class_('StopAction', automation.Action) | ||||
| RandomAction = dfplayer_ns.class_('RandomAction', automation.Action) | ||||
| SetDeviceAction = dfplayer_ns.class_('SetDeviceAction', automation.Action) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All(cv.Schema({ | ||||
|     cv.GenerateID(): cv.declare_id(DFPlayer), | ||||
|     cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({ | ||||
|         cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DFPlayerFinishedPlaybackTrigger), | ||||
|     }), | ||||
| }).extend(uart.UART_DEVICE_SCHEMA)) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield uart.register_uart_device(var, config) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_FINISHED_PLAYBACK, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         yield automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.play_next', NextAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_next_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.play_previous', PreviousAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_previous_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.play', PlayFileAction, cv.maybe_simple_value({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
|     cv.Required(CONF_FILE): cv.templatable(cv.int_), | ||||
|     cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), | ||||
| }, key=CONF_FILE)) | ||||
| def dfplayer_play_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     template_ = yield cg.templatable(config[CONF_FILE], args, float) | ||||
|     cg.add(var.set_file(template_)) | ||||
|     if CONF_LOOP in config: | ||||
|         template_ = yield cg.templatable(config[CONF_LOOP], args, float) | ||||
|         cg.add(var.set_loop(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.play_folder', PlayFolderAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
|     cv.Required(CONF_FOLDER): cv.templatable(cv.int_), | ||||
|     cv.Optional(CONF_FILE): cv.templatable(cv.int_), | ||||
|     cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), | ||||
| })) | ||||
| def dfplayer_play_folder_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     template_ = yield cg.templatable(config[CONF_FOLDER], args, float) | ||||
|     cg.add(var.set_folder(template_)) | ||||
|     if CONF_FILE in config: | ||||
|         template_ = yield cg.templatable(config[CONF_FILE], args, float) | ||||
|         cg.add(var.set_file(template_)) | ||||
|     if CONF_LOOP in config: | ||||
|         template_ = yield cg.templatable(config[CONF_LOOP], args, float) | ||||
|         cg.add(var.set_loop(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.set_device', SetDeviceAction, cv.maybe_simple_value({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
|     cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True), | ||||
| }, key=CONF_DEVICE)) | ||||
| def dfplayer_set_device_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     template_ = yield cg.templatable(config[CONF_DEVICE], args, Device) | ||||
|     cg.add(var.set_device(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.set_volume', SetVolumeAction, cv.maybe_simple_value({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
|     cv.Required(CONF_VOLUME): cv.templatable(cv.int_), | ||||
| }, key=CONF_VOLUME)) | ||||
| def dfplayer_set_volume_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     template_ = yield cg.templatable(config[CONF_VOLUME], args, float) | ||||
|     cg.add(var.set_volume(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.set_eq', SetEqAction, cv.maybe_simple_value({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
|     cv.Required(CONF_EQ_PRESET): cv.templatable(cv.enum(EQ_PRESET, upper=True)), | ||||
| }, key=CONF_EQ_PRESET)) | ||||
| def dfplayer_set_eq_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     template_ = yield cg.templatable(config[CONF_EQ_PRESET], args, EqPreset) | ||||
|     cg.add(var.set_eq(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.sleep', SleepAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_sleep_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.reset', ResetAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_reset_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.start', StartAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_start_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.pause', PauseAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_pause_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.stop', StopAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_stop_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_action('dfplayer.random', RandomAction, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplayer_random_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
| @automation.register_condition('dfplayer.is_playing', DFPlayerIsPlayingCondition, cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(DFPlayer), | ||||
| })) | ||||
| def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(condition_id, template_arg) | ||||
|     yield cg.register_parented(var, config[CONF_ID]) | ||||
|     yield var | ||||
							
								
								
									
										120
									
								
								esphome/components/dfplayer/dfplayer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								esphome/components/dfplayer/dfplayer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| #include "dfplayer.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace dfplayer { | ||||
|  | ||||
| static const char* TAG = "dfplayer"; | ||||
|  | ||||
| void DFPlayer::play_folder(uint16_t folder, uint16_t file) { | ||||
|   if (folder < 100 && file < 256) { | ||||
|     this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file); | ||||
|   } else if (folder <= 10 && file <= 1000) { | ||||
|     this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file); | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "Cannot play folder %d file %d.", folder, file); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) { | ||||
|   uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t)(argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef}; | ||||
|   uint16_t checksum = 0; | ||||
|   for (uint8_t i = 1; i < 7; i++) | ||||
|     checksum += buffer[i]; | ||||
|   checksum = -checksum; | ||||
|   buffer[7] = checksum >> 8; | ||||
|   buffer[8] = (uint8_t) checksum; | ||||
|  | ||||
|   this->sent_cmd_ = cmd; | ||||
|  | ||||
|   ESP_LOGD(TAG, "Send Command %#02x arg %#04x", cmd, argument); | ||||
|   this->write_array(buffer, 10); | ||||
| } | ||||
|  | ||||
| void DFPlayer::loop() { | ||||
|   // Read message | ||||
|   while (this->available()) { | ||||
|     uint8_t byte; | ||||
|     this->read_byte(&byte); | ||||
|  | ||||
|     if (this->read_pos_ == DFPLAYER_READ_BUFFER_LENGTH) | ||||
|       this->read_pos_ = 0; | ||||
|  | ||||
|     switch (this->read_pos_) { | ||||
|       case 0:  // Start mark | ||||
|         if (byte != 0x7E) | ||||
|           continue; | ||||
|         break; | ||||
|       case 1:  // Version | ||||
|         if (byte != 0xFF) { | ||||
|           ESP_LOGW(TAG, "Expected Version 0xFF, got %#02x", byte); | ||||
|           this->read_pos_ = 0; | ||||
|           continue; | ||||
|         } | ||||
|         break; | ||||
|       case 2:  // Buffer length | ||||
|         if (byte != 0x06) { | ||||
|           ESP_LOGW(TAG, "Expected Buffer length 0x06, got %#02x", byte); | ||||
|           this->read_pos_ = 0; | ||||
|           continue; | ||||
|         } | ||||
|         break; | ||||
|       case 9:  // End byte | ||||
|         if (byte != 0xEF) { | ||||
|           ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte); | ||||
|           this->read_pos_ = 0; | ||||
|           continue; | ||||
|         } | ||||
|         // Parse valid received command | ||||
|         uint8_t cmd = this->read_buffer_[3]; | ||||
|         uint16_t argument = (this->read_buffer_[5] << 8) | this->read_buffer_[6]; | ||||
|  | ||||
|         ESP_LOGV(TAG, "Received message cmd: %#02x arg %#04x", cmd, argument); | ||||
|  | ||||
|         switch (cmd) { | ||||
|           case 0x3A: | ||||
|             if (argument == 1) { | ||||
|               ESP_LOGI(TAG, "USB loaded"); | ||||
|             } else if (argument == 2) | ||||
|               ESP_LOGI(TAG, "TF Card loaded"); | ||||
|             break; | ||||
|           case 0x3B: | ||||
|             if (argument == 1) { | ||||
|               ESP_LOGI(TAG, "USB unloaded"); | ||||
|             } else if (argument == 2) | ||||
|               ESP_LOGI(TAG, "TF Card unloaded"); | ||||
|             break; | ||||
|           case 0x3F: | ||||
|             if (argument == 1) { | ||||
|               ESP_LOGI(TAG, "USB available"); | ||||
|             } else if (argument == 2) { | ||||
|               ESP_LOGI(TAG, "TF Card available"); | ||||
|             } else if (argument == 3) { | ||||
|               ESP_LOGI(TAG, "USB, TF Card available"); | ||||
|             } | ||||
|             break; | ||||
|           case 0x41: | ||||
|             ESP_LOGV(TAG, "Ack ok"); | ||||
|             this->is_playing_ |= this->ack_set_is_playing_; | ||||
|             this->is_playing_ &= !this->ack_reset_is_playing_; | ||||
|             this->ack_set_is_playing_ = false; | ||||
|             this->ack_reset_is_playing_ = false; | ||||
|             break; | ||||
|           case 0x3D:  // Playback finished | ||||
|             this->is_playing_ = false; | ||||
|             this->on_finished_playback_callback_.call(); | ||||
|             break; | ||||
|           default: | ||||
|             ESP_LOGD(TAG, "Command %#02x arg %#04x", cmd, argument); | ||||
|         } | ||||
|         this->sent_cmd_ = 0; | ||||
|         this->read_pos_ = 0; | ||||
|         continue; | ||||
|     } | ||||
|     this->read_buffer_[this->read_pos_] = byte; | ||||
|     this->read_pos_++; | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace dfplayer | ||||
| }  // namespace esphome | ||||
							
								
								
									
										165
									
								
								esphome/components/dfplayer/dfplayer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								esphome/components/dfplayer/dfplayer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/core/automation.h" | ||||
|  | ||||
| const size_t DFPLAYER_READ_BUFFER_LENGTH = 25;  // two messages + some extra | ||||
|  | ||||
| namespace esphome { | ||||
| namespace dfplayer { | ||||
|  | ||||
| enum EqPreset { | ||||
|   NORMAL = 0, | ||||
|   POP = 1, | ||||
|   ROCK = 2, | ||||
|   JAZZ = 3, | ||||
|   CLASSIC = 4, | ||||
|   BASS = 5, | ||||
| }; | ||||
|  | ||||
| enum Device { | ||||
|   USB = 1, | ||||
|   TF_CARD = 2, | ||||
| }; | ||||
|  | ||||
| class DFPlayer : public uart::UARTDevice, public Component { | ||||
|  public: | ||||
|   void loop() override; | ||||
|  | ||||
|   void next() { this->send_cmd_(0x01); } | ||||
|   void previous() { this->send_cmd_(0x02); } | ||||
|   void play_file(uint16_t file) { | ||||
|     this->ack_set_is_playing_ = true; | ||||
|     this->send_cmd_(0x03, file); | ||||
|   } | ||||
|   void play_file_loop(uint16_t file) { this->send_cmd_(0x08, file); } | ||||
|   void play_folder(uint16_t folder, uint16_t file); | ||||
|   void play_folder_loop(uint16_t folder) { this->send_cmd_(0x17, folder); } | ||||
|   void volume_up() { this->send_cmd_(0x04); } | ||||
|   void volume_down() { this->send_cmd_(0x05); } | ||||
|   void set_device(Device device) { this->send_cmd_(0x09, device); } | ||||
|   void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); } | ||||
|   void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); } | ||||
|   void sleep() { this->send_cmd_(0x0A); } | ||||
|   void reset() { this->send_cmd_(0x0C); } | ||||
|   void start() { this->send_cmd_(0x0D); } | ||||
|   void pause() { | ||||
|     this->ack_reset_is_playing_ = true; | ||||
|     this->send_cmd_(0x0E); | ||||
|   } | ||||
|   void stop() { this->send_cmd_(0x16); } | ||||
|   void random() { this->send_cmd_(0x18); } | ||||
|  | ||||
|   bool is_playing() { return is_playing_; } | ||||
|  | ||||
|   void add_on_finished_playback_callback(std::function<void()> callback) { | ||||
|     this->on_finished_playback_callback_.add(std::move(callback)); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   void send_cmd_(uint8_t cmd, uint16_t argument = 0); | ||||
|   void send_cmd_(uint8_t cmd, uint16_t high, uint16_t low) { | ||||
|     this->send_cmd_(cmd, ((high & 0xFF) << 8) | (low & 0xFF)); | ||||
|   } | ||||
|   uint8_t sent_cmd_{0}; | ||||
|  | ||||
|   char read_buffer_[DFPLAYER_READ_BUFFER_LENGTH]; | ||||
|   size_t read_pos_{0}; | ||||
|  | ||||
|   bool is_playing_{false}; | ||||
|   bool ack_set_is_playing_{false}; | ||||
|   bool ack_reset_is_playing_{false}; | ||||
|  | ||||
|   CallbackManager<void()> on_finished_playback_callback_; | ||||
| }; | ||||
|  | ||||
| #define DFPLAYER_SIMPLE_ACTION(ACTION_CLASS, ACTION_METHOD) \ | ||||
|   template<typename... Ts> class ACTION_CLASS : public Action<Ts...>, public Parented<DFPlayer> { \ | ||||
|    public: \ | ||||
|     void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \ | ||||
|   }; | ||||
|  | ||||
| DFPLAYER_SIMPLE_ACTION(NextAction, next) | ||||
| DFPLAYER_SIMPLE_ACTION(PreviousAction, previous) | ||||
|  | ||||
| template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint16_t, file) | ||||
|   TEMPLATABLE_VALUE(boolean, loop) | ||||
|   void play(Ts... x) override { | ||||
|     auto file = this->file_.value(x...); | ||||
|     auto loop = this->loop_.value(x...); | ||||
|     if (loop) { | ||||
|       this->parent_->play_file_loop(file); | ||||
|     } else { | ||||
|       this->parent_->play_file(file); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class PlayFolderAction : public Action<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint16_t, folder) | ||||
|   TEMPLATABLE_VALUE(uint16_t, file) | ||||
|   TEMPLATABLE_VALUE(boolean, loop) | ||||
|   void play(Ts... x) override { | ||||
|     auto folder = this->folder_.value(x...); | ||||
|     auto file = this->file_.value(x...); | ||||
|     auto loop = this->loop_.value(x...); | ||||
|     if (loop) { | ||||
|       this->parent_->play_folder_loop(folder); | ||||
|     } else { | ||||
|       this->parent_->play_folder(folder, file); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetDeviceAction : public Action<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(Device, device) | ||||
|   void play(Ts... x) override { | ||||
|     auto device = this->device_.value(x...); | ||||
|     this->parent_->set_device(device); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetVolumeAction : public Action<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint8_t, volume) | ||||
|   void play(Ts... x) override { | ||||
|     auto volume = this->volume_.value(x...); | ||||
|     this->parent_->set_volume(volume); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetEqAction : public Action<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(EqPreset, eq) | ||||
|   void play(Ts... x) override { | ||||
|     auto eq = this->eq_.value(x...); | ||||
|     this->parent_->set_eq(eq); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| DFPLAYER_SIMPLE_ACTION(SleepAction, sleep) | ||||
| DFPLAYER_SIMPLE_ACTION(ResetAction, reset) | ||||
| DFPLAYER_SIMPLE_ACTION(StartAction, start) | ||||
| DFPLAYER_SIMPLE_ACTION(PauseAction, pause) | ||||
| DFPLAYER_SIMPLE_ACTION(StopAction, stop) | ||||
| DFPLAYER_SIMPLE_ACTION(RandomAction, random) | ||||
|  | ||||
| template<typename... Ts> class DFPlayerIsPlayingCondition : public Condition<Ts...>, public Parented<DFPlayer> { | ||||
|  public: | ||||
|   bool check(Ts... x) override { return this->parent_->is_playing(); } | ||||
| }; | ||||
|  | ||||
| class DFPlayerFinishedPlaybackTrigger : public Trigger<> { | ||||
|  public: | ||||
|   explicit DFPlayerFinishedPlaybackTrigger(DFPlayer *parent) { | ||||
|     parent->add_on_finished_playback_callback([this]() { this->trigger(); }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace dfplayer | ||||
| }  // namespace esphome | ||||
| @@ -60,6 +60,83 @@ api: | ||||
|               - float_arr.size() | ||||
|               - string_arr[0].c_str() | ||||
|               - string_arr.size() | ||||
|     - service: dfplayer_next | ||||
|       then: | ||||
|         - dfplayer.play_next: | ||||
|     - service: dfplayer_previous | ||||
|       then: | ||||
|         - dfplayer.play_previous: | ||||
|     - service: dfplayer_play | ||||
|       variables: | ||||
|         file: int | ||||
|       then: | ||||
|         - dfplayer.play: !lambda 'return file;' | ||||
|     - service: dfplayer_play_loop | ||||
|       variables: | ||||
|         file: int | ||||
|         loop_: bool | ||||
|       then: | ||||
|         - dfplayer.play: | ||||
|             file: !lambda 'return file;' | ||||
|             loop: !lambda 'return loop_;' | ||||
|     - service: dfplayer_play_folder | ||||
|       variables: | ||||
|         folder: int | ||||
|         file: int | ||||
|       then: | ||||
|         - dfplayer.play_folder: | ||||
|             folder: !lambda 'return folder;' | ||||
|             file: !lambda 'return file;' | ||||
|  | ||||
|     - service: dfplayer_play_loo_folder | ||||
|       variables: | ||||
|         folder: int | ||||
|       then: | ||||
|         - dfplayer.play_folder: | ||||
|             folder: !lambda 'return folder;' | ||||
|             loop: True | ||||
|  | ||||
|     - service: dfplayer_set_device | ||||
|       variables: | ||||
|         device: int | ||||
|       then: | ||||
|         - dfplayer.set_device: | ||||
|             device: TF_CARD | ||||
|  | ||||
|     - service: dfplayer_set_volume | ||||
|       variables: | ||||
|         volume: int | ||||
|       then: | ||||
|         - dfplayer.set_volume: !lambda 'return volume;' | ||||
|     - service: dfplayer_set_eq | ||||
|       variables: | ||||
|         preset: int | ||||
|       then: | ||||
|         - dfplayer.set_eq: !lambda 'return static_cast<dfplayer::EqPreset>(preset);' | ||||
|  | ||||
|     - service: dfplayer_sleep | ||||
|       then: | ||||
|         - dfplayer.sleep | ||||
|  | ||||
|     - service: dfplayer_reset | ||||
|       then: | ||||
|         - dfplayer.reset | ||||
|  | ||||
|     - service: dfplayer_start | ||||
|       then: | ||||
|         - dfplayer.start | ||||
|  | ||||
|     - service: dfplayer_pause | ||||
|       then: | ||||
|         - dfplayer.pause | ||||
|  | ||||
|     - service: dfplayer_stop | ||||
|       then: | ||||
|         - dfplayer.stop | ||||
|  | ||||
|     - service: dfplayer_random | ||||
|       then: | ||||
|         - dfplayer.random | ||||
|  | ||||
| wifi: | ||||
|   ssid: 'MySSID' | ||||
| @@ -532,3 +609,13 @@ sim800l: | ||||
|     - sim800l.send_sms: | ||||
|         message: 'hello you' | ||||
|         recipient: '+1234' | ||||
|  | ||||
| dfplayer: | ||||
|   on_finished_playback: | ||||
|     then: | ||||
|       if: | ||||
|         condition: | ||||
|           not: | ||||
|             dfplayer.is_playing | ||||
|         then: | ||||
|           logger.log: 'Playback finished event' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user