mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 09:18:00 +01: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:
parent
89c1274d56
commit
54fe1c7d55
4 changed files with 593 additions and 0 deletions
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()
|
- float_arr.size()
|
||||||
- string_arr[0].c_str()
|
- string_arr[0].c_str()
|
||||||
- string_arr.size()
|
- 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:
|
wifi:
|
||||||
ssid: 'MySSID'
|
ssid: 'MySSID'
|
||||||
|
@ -532,3 +609,13 @@ sim800l:
|
||||||
- sim800l.send_sms:
|
- sim800l.send_sms:
|
||||||
message: 'hello you'
|
message: 'hello you'
|
||||||
recipient: '+1234'
|
recipient: '+1234'
|
||||||
|
|
||||||
|
dfplayer:
|
||||||
|
on_finished_playback:
|
||||||
|
then:
|
||||||
|
if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
dfplayer.is_playing
|
||||||
|
then:
|
||||||
|
logger.log: 'Playback finished event'
|
||||||
|
|
Loading…
Reference in a new issue