mirror of
https://github.com/esphome/esphome.git
synced 2025-03-13 22:28:14 +00:00
Revert "Dev"
This commit is contained in:
parent
264638dbca
commit
adfe27df92
@ -1,41 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, output
|
||||
from esphome.components.time import RealTimeClock
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ["i2c", "fram", "output", "time"]
|
||||
CODEOWNERS = ["@p1ngb4ck"]
|
||||
MULTI_CONF = False
|
||||
|
||||
fram_ns = cg.esphome_ns.namespace("fram")
|
||||
FRAMComponent = fram_ns.class_("FRAM", cg.Component, i2c.I2CDevice)
|
||||
dynamic_lamp_ns = cg.esphome_ns.namespace('dynamic_lamp')
|
||||
DynamicLampComponent = dynamic_lamp_ns.class_('DynamicLampComponent', cg.Component)
|
||||
CONF_DYNAMIC_LAMP_ID = "dynamic_lamp_id"
|
||||
|
||||
CONF_RTC = 'rtc'
|
||||
CONF_FRAM = 'fram'
|
||||
CONF_SAVE_MODE = 'save_mode'
|
||||
CONF_AVAILABLE_OUTPUTS = 'available_outputs'
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(DynamicLampComponent),
|
||||
cv.Required(CONF_RTC): cv.use_id(RealTimeClock),
|
||||
cv.Required(CONF_FRAM): cv.use_id(FRAMComponent),
|
||||
cv.Required(CONF_AVAILABLE_OUTPUTS): [cv.use_id(output.FloatOutput)],
|
||||
cv.Optional(CONF_SAVE_MODE, default=0): cv.int_range(0, 2),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
await cg.get_variable(config[CONF_RTC]),
|
||||
await cg.get_variable(config[CONF_FRAM]),
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
for outputPointer in config.get(CONF_AVAILABLE_OUTPUTS, []):
|
||||
idstr_ = str(outputPointer)
|
||||
output_ = await cg.get_variable(outputPointer)
|
||||
cg.add(var.add_available_output(output_, idstr_))
|
||||
cg.add(var.set_save_mode(config[CONF_SAVE_MODE]))
|
@ -1,524 +0,0 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "dynamic_lamp.h"
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <bit>
|
||||
#include <sstream>
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_lamp {
|
||||
|
||||
static const char *TAG = "dynamic_lamp";
|
||||
|
||||
void DynamicLampComponent::setup() {
|
||||
this->begin();
|
||||
}
|
||||
|
||||
void DynamicLampComponent::begin() {
|
||||
for (uint8_t i=0; i < 16; i++) {
|
||||
this->active_lamps_[i] = CombinedLamp();
|
||||
this->active_lamps_[i].active = false;
|
||||
this->available_outputs_[i] = LinkedOutput{ false, false, "", 0, nullptr, 0.0, 0, 0, 0.0f, 1.0f, false };
|
||||
}
|
||||
this->restore_lamp_settings_();
|
||||
this->restore_timers_();
|
||||
}
|
||||
|
||||
void DynamicLampComponent::loop() {
|
||||
uint8_t i = 0;
|
||||
for (i = 0; i < this->lamp_count_; i++) {
|
||||
if (this->active_lamps_[i].active == true && this->active_lamps_[i].update_ == true) {
|
||||
uint8_t j = 0;
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t k = 0;
|
||||
uint8_t l = j;
|
||||
if (j > 7) {
|
||||
k = 1;
|
||||
l = j - 8;
|
||||
}
|
||||
bool output_in_use = static_cast<bool>(this->active_lamps_[i].used_outputs[k] & (1 << l));
|
||||
if (output_in_use == true) {
|
||||
// Update level
|
||||
float new_state;
|
||||
new_state = this->active_lamps_[i].state_;
|
||||
switch (this->available_outputs_[j].mode) {
|
||||
case MODE_EQUAL:
|
||||
if (this->available_outputs_[j].min_value && new_state < *this->available_outputs_[j].min_value) {
|
||||
new_state = *this->available_outputs_[j].min_value;
|
||||
}
|
||||
else if (this->available_outputs_[j].max_value && new_state > *this->available_outputs_[j].max_value) {
|
||||
new_state = *this->available_outputs_[j].max_value;
|
||||
}
|
||||
break;
|
||||
case MODE_STATIC:
|
||||
new_state = this->available_outputs_[j].mode_value;
|
||||
break;
|
||||
case MODE_PERCENTAGE:
|
||||
new_state = this->active_lamps_[i].state_ * this->available_outputs_[j].mode_value;
|
||||
if (this->available_outputs_[j].min_value && new_state < *this->available_outputs_[j].min_value) {
|
||||
new_state = *this->available_outputs_[j].min_value;
|
||||
}
|
||||
else if (this->available_outputs_[j].max_value && new_state > *this->available_outputs_[j].max_value) {
|
||||
new_state = *this->available_outputs_[j].max_value;
|
||||
}
|
||||
break;
|
||||
case MODE_FUNCTION:
|
||||
// ToDo - yet to be implemented
|
||||
ESP_LOGW(TAG, "Mode %d for output %s is not implemented yet, sorry", this->available_outputs_[j].mode, this->available_outputs_[j].output_id.c_str());
|
||||
this->status_set_warning();
|
||||
continue;
|
||||
default:
|
||||
// Unknown
|
||||
ESP_LOGW(TAG, "Unknown mode %d for output %s", this->available_outputs_[j].mode, this->available_outputs_[j].output_id.c_str());
|
||||
this->status_set_warning();
|
||||
continue;
|
||||
}
|
||||
ESP_LOGV(TAG, "Setting output %s to level %f", this->available_outputs_[j].output_id.c_str(), new_state);
|
||||
this->available_outputs_[j].output->set_level(new_state);
|
||||
}
|
||||
}
|
||||
this->active_lamps_[i].update_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLampComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Dynamic Lamp feature loaded");
|
||||
switch(this->save_mode_) {
|
||||
case SAVE_MODE_NONE:
|
||||
ESP_LOGCONFIG(TAG, "Save mode set to NONE");
|
||||
break;
|
||||
case SAVE_MODE_LOCAL:
|
||||
ESP_LOGCONFIG(TAG, "Save mode set to LOCAL");
|
||||
break;
|
||||
case SAVE_MODE_FRAM:
|
||||
ESP_LOGCONFIG(TAG, "Save mode set to FRAM");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGCONFIG(TAG, "Currently only NONE(0), LOCAL(1) & FRAM(2) save modes supported, ignoring value %" PRIu8 " and defaulting to NONE!", this->save_mode_);
|
||||
this->save_mode_ = 0;
|
||||
}
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
if (this->available_outputs_[i].available == true) {
|
||||
ESP_LOGCONFIG(TAG, "Using output with id %s as output number %" PRIu8 "", this->available_outputs_[i].output_id.c_str(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLampComponent::set_save_mode(uint8_t save_mode) {
|
||||
this->save_mode_ = save_mode;
|
||||
}
|
||||
|
||||
void DynamicLampComponent::add_available_output(output::FloatOutput * output, std::string output_id) {
|
||||
uint8_t counter = 0;
|
||||
while (this->available_outputs_[counter].available == true) {
|
||||
counter++;
|
||||
}
|
||||
if (counter > 15) {
|
||||
ESP_LOGW(TAG, "No more outputs available, max 16 outputs supported!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
this->available_outputs_[counter].available = true;
|
||||
this->available_outputs_[counter].output_id = output_id;
|
||||
this->available_outputs_[counter].output = output;
|
||||
this->available_outputs_[counter].output_index = counter;
|
||||
counter++;
|
||||
}
|
||||
|
||||
void DynamicLampComponent::add_lamp(std::string name) {
|
||||
if (this->lamp_count_ < 15) {
|
||||
this->active_lamps_[this->lamp_count_].active = true;
|
||||
strncpy(reinterpret_cast<char*>(this->active_lamps_[this->lamp_count_].name), name.c_str(), 16);
|
||||
this->active_lamps_[this->lamp_count_].validation_byte = 'L';
|
||||
this->active_lamps_[this->lamp_count_].lamp_index = this->lamp_count_;
|
||||
this->active_lamps_[this->lamp_count_].used_outputs[0] = 0;
|
||||
this->active_lamps_[this->lamp_count_].used_outputs[1] = 0;
|
||||
this->fram_->write((0x0000 + (this->lamp_count_ * 24)), reinterpret_cast<unsigned char *>(&this->active_lamps_[this->lamp_count_]), 24);
|
||||
this->lamp_count_++;
|
||||
ESP_LOGV(TAG, "Added new lamp %s, total lamps now %" PRIu8 "", name.c_str(), this->lamp_count_);
|
||||
return;
|
||||
}
|
||||
ESP_LOGW(TAG, "No more lamps available, max 16 lamps supported!");
|
||||
this->status_set_warning();
|
||||
}
|
||||
|
||||
void DynamicLampComponent::remove_lamp(std::string lamp_name) {
|
||||
uint8_t i = 0;
|
||||
while (i < this->lamp_count_) {
|
||||
std::string str(this->active_lamps_[i].name, this->active_lamps_[i].name + sizeof this->active_lamps_[i].name / sizeof this->active_lamps_[i].name[0]);
|
||||
if (str == lamp_name) {
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
uint8_t k = 0;
|
||||
uint8_t l = j;
|
||||
if (j > 7) {
|
||||
k = 1;
|
||||
l = j - 8;
|
||||
}
|
||||
bool output_in_use = static_cast<bool>(this->active_lamps_[i].used_outputs[k] & (1 << l));
|
||||
if (output_in_use == true) {
|
||||
this->available_outputs_[j].in_use = false;
|
||||
this->active_lamps_[i].used_outputs[j] = false;
|
||||
}
|
||||
}
|
||||
uint16_t memaddress = 0 + (i * 24);
|
||||
unsigned char empty_lamp[24];
|
||||
for (uint8_t m = 0; m < 24; m++) {
|
||||
empty_lamp[m] = 0xff;
|
||||
}
|
||||
this->fram_->write(memaddress, empty_lamp, 24);
|
||||
this->active_lamps_[i].active = false;
|
||||
this->lamp_count_--;
|
||||
ESP_LOGV(TAG, "Removed lamp %s, total lamps now %" PRIu8 "", this->active_lamps_[i].name, this->lamp_count_);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "No lamp with name %s defined !", lamp_name.c_str());
|
||||
}
|
||||
|
||||
void DynamicLampComponent::add_output_to_lamp(std::string lamp_name, LinkedOutput *output) {
|
||||
if (output->available == false) {
|
||||
ESP_LOGW(TAG, "Output %s is not available, ignoring!", output->output_id.c_str());
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
if (output->in_use == true) {
|
||||
ESP_LOGW(TAG, "Output %s is already in use, ignoring!", output->output_id.c_str());
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
uint8_t i = 0;
|
||||
while (i < 16) {
|
||||
std::string str(this->active_lamps_[i].name, this->active_lamps_[i].name + sizeof this->active_lamps_[i].name / sizeof this->active_lamps_[i].name[0]);
|
||||
if (str == lamp_name) {
|
||||
uint8_t j = 0;
|
||||
if (output->output_index > 7) {
|
||||
j = 1;
|
||||
}
|
||||
this->active_lamps_[i].used_outputs[j] |= 1 << (output->output_index % 8);
|
||||
output->in_use = true;
|
||||
this->fram_->write((0x0000 + (this->active_lamps_[i].lamp_index * 24)), reinterpret_cast<unsigned char *>(&this->active_lamps_[i]), 24);
|
||||
ESP_LOGV(TAG, "Added output %s to lamp %s", output->output_id.c_str(), lamp_name.c_str());
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "No lamp with name %s defined !", lamp_name.c_str());
|
||||
}
|
||||
|
||||
void DynamicLampComponent::remove_output_from_lamp(std::string lamp_name, LinkedOutput *output) {
|
||||
uint8_t i = 0;
|
||||
while (i < 16) {
|
||||
std::string str(this->active_lamps_[i].name, this->active_lamps_[i].name + sizeof this->active_lamps_[i].name / sizeof this->active_lamps_[i].name[0]);
|
||||
if (str == lamp_name) {
|
||||
uint8_t k = output->output_index;
|
||||
uint8_t j = 0;
|
||||
if (output->output_index > 7) {
|
||||
j = 1;
|
||||
k = output->output_index - 8;
|
||||
}
|
||||
this->active_lamps_[i].used_outputs[j] &= ~(1 << k);
|
||||
this->fram_->write((0x0000 + (i * 24)), reinterpret_cast<unsigned char *>(&this->active_lamps_[i]), 24);
|
||||
output->in_use = false;
|
||||
ESP_LOGV(TAG, "Removed output %s from lamp %s", output->output_id.c_str(), lamp_name.c_str());
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "No lamp with name %s defined !", lamp_name.c_str());
|
||||
}
|
||||
|
||||
std::array<bool, 16> DynamicLampComponent::get_lamp_outputs(uint8_t lamp_number) {
|
||||
std::array<bool, 16> bool_array;
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
uint8_t j = 0;
|
||||
uint8_t k = i;
|
||||
if (i > 7) {
|
||||
j = 1;
|
||||
k = i - 8;
|
||||
}
|
||||
bool_array[i] = static_cast<bool>(this->active_lamps_[lamp_number].used_outputs[j] & (1 << k));
|
||||
}
|
||||
return bool_array;
|
||||
}
|
||||
|
||||
uint8_t DynamicLampComponent::get_lamp_index_by_name_(std::string lamp_name) {
|
||||
uint8_t i = 0;
|
||||
for (i = 0; i < this->lamp_count_; i++) {
|
||||
std::string str(this->active_lamps_[i].name, this->active_lamps_[i].name + sizeof this->active_lamps_[i].name / sizeof this->active_lamps_[i].name[0]);
|
||||
if (str == lamp_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "No lamp with name %s defined !", lamp_name.c_str());
|
||||
return 255;
|
||||
}
|
||||
|
||||
std::array<bool, 16> DynamicLampComponent::get_lamp_outputs_by_name_(std::string lamp_name) {
|
||||
uint8_t lamp_index = this->get_lamp_index_by_name_(lamp_name);
|
||||
if (lamp_index == 255) {
|
||||
std::array<bool, 16> bool_array;
|
||||
return bool_array;
|
||||
}
|
||||
return this->get_lamp_outputs(lamp_index);
|
||||
}
|
||||
|
||||
bool DynamicLampComponent::add_timer(std::string timer_desc, std::string lamp_list_str, bool timer_active, uint8_t action, uint8_t hour,
|
||||
uint8_t minute, bool monday, bool tuesday, bool wednesday, bool thursday,
|
||||
bool friday, bool saturday, bool sunday) {
|
||||
std::vector<bool> lamp_list = this->build_lamp_list_from_list_str_(lamp_list_str);
|
||||
DynamicLampTimer new_timer;
|
||||
strncpy(reinterpret_cast<char *>(new_timer.timer_desc), timer_desc.c_str(), 36);
|
||||
unsigned char lamp_list_bytes[2] = {0, 0};
|
||||
for (uint8_t i = 0; i < lamp_list.size(); i++) {
|
||||
if (lamp_list[i] == true && this->active_lamps_[i].active != true) {
|
||||
ESP_LOGW(TAG, "Ignoring lamp number %" PRIu8 " as there is no active lamp with that index!", i);
|
||||
continue;
|
||||
}
|
||||
if (lamp_list[i] == true) {
|
||||
lamp_list_bytes[i / 8] |= 1 << (i % 8);
|
||||
}
|
||||
}
|
||||
memcpy(new_timer.lamp_list, lamp_list_bytes, 2);
|
||||
new_timer.in_use = true;
|
||||
new_timer.validation_bytes[0] = 'V';
|
||||
new_timer.validation_bytes[1] = 'D';
|
||||
new_timer.validation_bytes[2] = 'L';
|
||||
new_timer.validation_bytes[3] = 'T';
|
||||
new_timer.active = timer_active;
|
||||
new_timer.action = action;
|
||||
new_timer.hour = hour;
|
||||
new_timer.minute = minute;
|
||||
new_timer.monday = monday;
|
||||
new_timer.tuesday = tuesday;
|
||||
new_timer.wednesday = wednesday;
|
||||
new_timer.thursday = thursday;
|
||||
new_timer.friday = friday;
|
||||
new_timer.saturday = saturday;
|
||||
new_timer.sunday = sunday;
|
||||
ESPTime now = this->rtc_->now();
|
||||
time_t begin_date = now.timestamp;
|
||||
now.increment_day();
|
||||
time_t end_date = now.timestamp;
|
||||
new_timer.begin_date = begin_date;
|
||||
new_timer.end_date = end_date;
|
||||
unsigned char* timer_as_bytes = static_cast<unsigned char*>(static_cast<void*>(&new_timer));
|
||||
ESP_LOGV(TAG, "Added new timer %s with lamp-list %s, active %d, action %d, hour %d, minute %d, monday %d, tuesday %d, wednesday %d, thursday %d, friday %d, saturday %d, sunday %d",
|
||||
new_timer.timer_desc, lamp_list_str.c_str(), new_timer.active, new_timer.action, new_timer.hour, new_timer.minute, new_timer.monday,
|
||||
new_timer.tuesday, new_timer.wednesday, new_timer.thursday, new_timer.friday, new_timer.saturday, new_timer.sunday);
|
||||
uint8_t save_slot;
|
||||
for (save_slot = 0; save_slot < 128; save_slot++) {
|
||||
if (this->timers_[save_slot].in_use != true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (save_slot == 128) {
|
||||
ESP_LOGW(TAG, "No more timer slots available, max 128 timers supported!");
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
this->timers_[save_slot] = new_timer;
|
||||
this->fram_->write((0x4000 + (save_slot * 64)), timer_as_bytes, 64);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<bool> DynamicLampComponent::build_lamp_list_from_list_str_(std::string lamp_list_str) {
|
||||
std::vector<uint8_t> lamp_list_vector = this->split_to_int_vector_(lamp_list_str);
|
||||
std::vector<bool> lamp_list;
|
||||
while (lamp_list.size() < 16) {
|
||||
lamp_list.push_back(false);
|
||||
}
|
||||
if (lamp_list_vector.size() > 16) {
|
||||
ESP_LOGW(TAG, "Too many lamps in list, only 16 supported!");
|
||||
this->status_set_warning();
|
||||
return lamp_list;
|
||||
}
|
||||
for (uint8_t i = 0; i < lamp_list_vector.size(); i++) {
|
||||
uint8_t lamp_index = lamp_list_vector[i];
|
||||
if (lamp_index > 15) {
|
||||
ESP_LOGW(TAG, "Lamp index %" PRIu8 " is out of range, only [0-15] supported!", lamp_list_vector[i]);
|
||||
this->status_set_warning();
|
||||
return lamp_list;
|
||||
}
|
||||
lamp_list[lamp_index] = true;
|
||||
}
|
||||
return lamp_list;
|
||||
}
|
||||
|
||||
void DynamicLampComponent::read_fram_timers_to_log() {
|
||||
DynamicLampTimer timer;
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
this->fram_->read((0x4000 + (i * 64)), reinterpret_cast<unsigned char *>(&timer), 64);
|
||||
if (timer.validation_bytes[0] == 'V' && timer.validation_bytes[1] == 'D' && timer.validation_bytes[2] == 'L' && timer.validation_bytes[3] == 'T' && timer.in_use == true) {
|
||||
std::string lamp_names_str = "";
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
uint8_t k = 0;
|
||||
uint8_t l = j;
|
||||
if (j > 7) {
|
||||
k = 1;
|
||||
l = j - 8;
|
||||
}
|
||||
//bool lamp_included = static_cast<bool>(timer.lamp_list[j / 8] & (1 << (j % 8)));
|
||||
bool lamp_included = static_cast<bool>(timer.lamp_list[k] & (1 << l));
|
||||
if (lamp_included == true && this->active_lamps_[j].active == true) {
|
||||
if (lamp_names_str.length() > 0) {
|
||||
lamp_names_str += ", ";
|
||||
}
|
||||
std::string str(this->active_lamps_[j].name, this->active_lamps_[j].name + sizeof this->active_lamps_[j].name / sizeof this->active_lamps_[j].name[0]);
|
||||
lamp_names_str += str;
|
||||
}
|
||||
}
|
||||
ESP_LOGV(TAG, "Timer %s found: [ active: %d, action: %d, hour: %d, minute: %d, monday: %d, tuesday: %d, wednesday: %d, thursday: %d, friday: %d, saturday: %d, sunday: %d ]",
|
||||
timer.timer_desc, timer.active, timer.action, timer.hour, timer.minute, timer.monday, timer.tuesday,
|
||||
timer.wednesday, timer.thursday, timer.friday, timer.saturday, timer.sunday);
|
||||
ESP_LOGV(TAG, "Timer active for lamps %s", lamp_names_str.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLampComponent::read_initialized_timers_to_log() {
|
||||
DynamicLampTimer timer;
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
if (this->timers_[i].in_use == true) {
|
||||
timer = this->timers_[i];
|
||||
std::string lamp_names_str = "";
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
uint8_t k = 0;
|
||||
uint8_t l = j;
|
||||
if (j > 7) {
|
||||
k = 1;
|
||||
l = j - 8;
|
||||
}
|
||||
//bool lamp_included = static_cast<bool>(timer.lamp_list[j / 8] & (1 << (j % 8)));
|
||||
bool lamp_included = static_cast<bool>(timer.lamp_list[k] & (1 << l));
|
||||
if (lamp_included == true && this->active_lamps_[j].active == true) {
|
||||
if (lamp_names_str.length() > 0) {
|
||||
lamp_names_str += ", ";
|
||||
}
|
||||
std::string str(this->active_lamps_[j].name, this->active_lamps_[j].name + sizeof this->active_lamps_[j].name / sizeof this->active_lamps_[j].name[0]);
|
||||
lamp_names_str += str;
|
||||
}
|
||||
}
|
||||
ESP_LOGV(TAG, "Timer %s found: [ active: %d, action: %d, hour: %d, minute: %d, monday: %d, tuesday: %d, wednesday: %d, thursday: %d, friday: %d, saturday: %d, sunday: %d ]",
|
||||
timer.timer_desc, timer.active, timer.action, timer.hour, timer.minute, timer.monday, timer.tuesday,
|
||||
timer.wednesday, timer.thursday, timer.friday, timer.saturday, timer.sunday);
|
||||
ESP_LOGV(TAG, "Timer active for lamps %s", lamp_names_str.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicLampComponent::write_state_(uint8_t lamp_number, float state) {
|
||||
if (this->active_lamps_[lamp_number].active == true) {
|
||||
this->active_lamps_[lamp_number].state_ = state;
|
||||
this->active_lamps_[lamp_number].update_ = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DynamicLampComponent::get_lamp_name(uint8_t lamp_number) {
|
||||
std::string str(this->active_lamps_[lamp_number].name, this->active_lamps_[lamp_number].name + sizeof this->active_lamps_[lamp_number].name / sizeof this->active_lamps_[lamp_number].name[0]);
|
||||
return str;
|
||||
}
|
||||
|
||||
void DynamicLampComponent::set_lamp_values_(uint8_t lamp_number, bool active, uint16_t selected_outputs, uint8_t mode, uint8_t mode_value) {
|
||||
|
||||
}
|
||||
|
||||
void DynamicLampComponent::restore_lamp_settings_() {
|
||||
switch (this->save_mode_) {
|
||||
case SAVE_MODE_LOCAL:
|
||||
// ToDo - yet to be implemented
|
||||
ESP_LOGW(TAG, "Save mode LOCAL not implemented yet, sorry");
|
||||
this->status_set_warning();
|
||||
break;
|
||||
case SAVE_MODE_FRAM:
|
||||
CombinedLamp lamp;
|
||||
for (uint8_t i=0; i < 16; i++) {
|
||||
this->fram_->read((0x0000 + (i * 24)), reinterpret_cast<unsigned char *>(&lamp), 24);
|
||||
if (lamp.validation_byte == 'L' && lamp.active == true) {
|
||||
this->active_lamps_[i] = lamp;
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
uint8_t k = 0;
|
||||
uint8_t l = j;
|
||||
if (j > 7) {
|
||||
k = 1;
|
||||
l = j - 8;
|
||||
}
|
||||
bool output_in_use = static_cast<bool>(lamp.used_outputs[k] & (1 << l));
|
||||
if (output_in_use == true) {
|
||||
this->available_outputs_[j].in_use = true;
|
||||
}
|
||||
}
|
||||
this->lamp_count_++;
|
||||
} else {
|
||||
lamp = CombinedLamp();
|
||||
this->active_lamps_[i] = lamp;
|
||||
this->active_lamps_[i].active = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLampComponent::restore_timers_() {
|
||||
DynamicLampTimer timer;
|
||||
switch (this->save_mode_) {
|
||||
case SupportedSaveModes::SAVE_MODE_NONE:
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
timer = DynamicLampTimer();
|
||||
this->timers_[i] = timer;
|
||||
this->timers_[i].in_use = false;
|
||||
}
|
||||
break;
|
||||
case SupportedSaveModes::SAVE_MODE_LOCAL:
|
||||
// ToDo - yet to be implemented
|
||||
ESP_LOGW(TAG, "Save mode LOCAL not implemented yet, sorry");
|
||||
this->status_set_warning();
|
||||
break;
|
||||
case SupportedSaveModes::SAVE_MODE_FRAM:
|
||||
std::string lamp_names_str;
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
this->fram_->read((0x4000 + (i * 64)), reinterpret_cast<unsigned char *>(&timer), 64);
|
||||
if (timer.validation_bytes[0] == 'V' && timer.validation_bytes[1] == 'D' && timer.validation_bytes[2] == 'L' && timer.validation_bytes[3] == 'T' && timer.in_use == true) {
|
||||
this->timers_[i] = timer;
|
||||
} else {
|
||||
timer = DynamicLampTimer();
|
||||
this->timers_[i] = timer;
|
||||
this->timers_[i].in_use = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLampComponent::clear_fram() {
|
||||
this->fram_->clear();
|
||||
ESP_LOGV(TAG, "Cleared FRAM");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> DynamicLampComponent::split_to_int_vector_(std::string lamp_list_str) {
|
||||
std::vector<uint8_t> tokens;
|
||||
std::stringstream sstream(lamp_list_str);
|
||||
std::string segment;
|
||||
while(std::getline(sstream, segment, ',')) {
|
||||
tokens.push_back(static_cast<uint8_t>(atoi(segment.c_str())));
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
} // namespace dynamic_lamp
|
||||
} // namespace esphome
|
@ -1,140 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/optional.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/time.h"
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
#include "esphome/components/fram/FRAM.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_lamp {
|
||||
|
||||
enum SupportedSaveModes : uint8_t {
|
||||
SAVE_MODE_NONE = 0,
|
||||
SAVE_MODE_LOCAL = 1,
|
||||
SAVE_MODE_FRAM = 2
|
||||
};
|
||||
|
||||
enum LinkedOutputModeIdx : uint8_t {
|
||||
MODE_EQUAL = 0,
|
||||
MODE_STATIC = 1,
|
||||
MODE_PERCENTAGE = 2,
|
||||
MODE_FUNCTION = 3
|
||||
};
|
||||
|
||||
struct LinkedOutput {
|
||||
bool available = false;
|
||||
bool in_use = false;
|
||||
std::string output_id;
|
||||
uint8_t output_index;
|
||||
output::FloatOutput *output;
|
||||
float state;
|
||||
uint8_t mode = 0;
|
||||
float mode_value = 0;
|
||||
optional<float> min_value;
|
||||
optional<float> max_value;
|
||||
bool update_level = false;
|
||||
};
|
||||
|
||||
enum DynamicLampIdx : uint8_t {
|
||||
LAMP_1 = 0,
|
||||
LAMP_2 = 1,
|
||||
LAMP_3 = 2,
|
||||
LAMP_4 = 3,
|
||||
LAMP_5 = 4,
|
||||
LAMP_6 = 5,
|
||||
LAMP_7 = 6,
|
||||
LAMP_8 = 7,
|
||||
LAMP_9 = 8,
|
||||
LAMP_10 = 9,
|
||||
LAMP_11 = 10,
|
||||
LAMP_12 = 11,
|
||||
LAMP_13 = 12,
|
||||
LAMP_14 = 13,
|
||||
LAMP_15 = 14,
|
||||
LAMP_16 = 15,
|
||||
};
|
||||
|
||||
struct CombinedLamp {
|
||||
unsigned char validation_byte;
|
||||
uint8_t lamp_index : 4;
|
||||
bool active : 1;
|
||||
bool update_ : 1;
|
||||
unsigned char :0;
|
||||
unsigned char name[16];
|
||||
float state_;
|
||||
unsigned char used_outputs[2];
|
||||
};
|
||||
|
||||
struct DynamicLampTimer {
|
||||
unsigned char validation_bytes[4];
|
||||
unsigned char timer_desc[36];
|
||||
unsigned char lamp_list[2];
|
||||
bool in_use : 1;
|
||||
uint8_t action : 3;
|
||||
uint8_t hour : 5;
|
||||
uint8_t minute : 6;
|
||||
bool active : 1;
|
||||
bool monday : 1;
|
||||
bool tuesday : 1;
|
||||
bool wednesday : 1;
|
||||
bool thursday : 1;
|
||||
bool friday : 1;
|
||||
bool saturday : 1;
|
||||
bool sunday : 1;
|
||||
bool respect_dst : 1;
|
||||
time_t begin_date : 64;
|
||||
time_t end_date : 64;
|
||||
};
|
||||
|
||||
class DynamicLamp;
|
||||
|
||||
class DynamicLampComponent : public Component {
|
||||
public:
|
||||
explicit DynamicLampComponent(time::RealTimeClock *rtc, fram::FRAM *fram) : rtc_(rtc), fram_(fram) {}
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
void begin();
|
||||
void add_available_output(output::FloatOutput * output, std::string output_id);
|
||||
void set_save_mode(uint8_t save_mode);
|
||||
void add_lamp(std::string name);
|
||||
void remove_lamp(std::string name);
|
||||
std::string get_lamp_name(uint8_t lamp_number);
|
||||
void add_output_to_lamp(std::string lamp_name, LinkedOutput *output);
|
||||
void remove_output_from_lamp(std::string lamp_name, LinkedOutput *output);
|
||||
std::array<bool, 16> get_lamp_outputs(uint8_t lamp_number);
|
||||
bool add_timer(std::string timer_desc, std::string lamp_name, bool timer_active, uint8_t mode, uint8_t hour,
|
||||
uint8_t minute, bool monday, bool tuesday, bool wednesday, bool thursday,
|
||||
bool friday, bool saturday, bool sunday);
|
||||
void read_initialized_timers_to_log();
|
||||
void read_fram_timers_to_log();
|
||||
void clear_fram();
|
||||
|
||||
protected:
|
||||
friend class DynamicLamp;
|
||||
time::RealTimeClock *rtc_;
|
||||
fram::FRAM *fram_;
|
||||
void restore_lamp_settings_();
|
||||
void restore_timers_();
|
||||
void set_lamp_values_(uint8_t lamp_number, bool active, uint16_t selected_outputs, uint8_t mode, uint8_t mode_value);
|
||||
bool write_state_(uint8_t lamp_number, float state);
|
||||
uint8_t get_lamp_index_by_name_(std::string lamp_name);
|
||||
std::array<bool, 16> get_lamp_outputs_by_name_(std::string lamp_name);
|
||||
std::vector<uint8_t> split_to_int_vector_(std::string lamp_list_str);
|
||||
std::vector<bool> build_lamp_list_from_list_str_(std::string lamp_list_str);
|
||||
|
||||
CombinedLamp active_lamps_[16];
|
||||
LinkedOutput available_outputs_[16];
|
||||
DynamicLampTimer timers_[128];
|
||||
uint8_t save_mode_;
|
||||
uint8_t lamp_count_ = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace dynamic_lamp
|
||||
} // namespace esphome
|
@ -1,51 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
|
||||
from .. import CONF_DYNAMIC_LAMP_ID, DynamicLampComponent, dynamic_lamp_ns
|
||||
|
||||
DEPENDENCIES = ["dynamic_lamp"]
|
||||
|
||||
DynamicLamp = dynamic_lamp_ns.class_(
|
||||
"DynamicLamp", output.FloatOutput, cg.Parented.template(DynamicLampComponent)
|
||||
)
|
||||
|
||||
DynamicLampIdx = dynamic_lamp_ns.enum("DynamicLampIdx")
|
||||
CHANNEL_OPTIONS = {
|
||||
"A": DynamicLampIdx.LAMP_1,
|
||||
"B": DynamicLampIdx.LAMP_2,
|
||||
"C": DynamicLampIdx.LAMP_3,
|
||||
"D": DynamicLampIdx.LAMP_4,
|
||||
"E": DynamicLampIdx.LAMP_5,
|
||||
"F": DynamicLampIdx.LAMP_6,
|
||||
"G": DynamicLampIdx.LAMP_7,
|
||||
"H": DynamicLampIdx.LAMP_8,
|
||||
"I": DynamicLampIdx.LAMP_9,
|
||||
"J": DynamicLampIdx.LAMP_10,
|
||||
"K": DynamicLampIdx.LAMP_11,
|
||||
"L": DynamicLampIdx.LAMP_12,
|
||||
"M": DynamicLampIdx.LAMP_13,
|
||||
"N": DynamicLampIdx.LAMP_14,
|
||||
"O": DynamicLampIdx.LAMP_15,
|
||||
"P": DynamicLampIdx.LAMP_16,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(DynamicLamp),
|
||||
cv.GenerateID(CONF_DYNAMIC_LAMP_ID): cv.use_id(DynamicLampComponent),
|
||||
cv.Required(CONF_CHANNEL): cv.enum(CHANNEL_OPTIONS, upper=True),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_DYNAMIC_LAMP_ID])
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
parent,
|
||||
config[CONF_CHANNEL],
|
||||
)
|
||||
await output.register_output(var, config)
|
||||
await cg.register_parented(var, config[CONF_DYNAMIC_LAMP_ID])
|
@ -1,14 +0,0 @@
|
||||
#include "dynamic_lamp_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_lamp {
|
||||
|
||||
void DynamicLamp::write_state(float state) {
|
||||
if (this->parent_->write_state_(this->lamp_, state))
|
||||
{
|
||||
this->state_ = state;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dynamic_lamp
|
||||
} // namespace esphome
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../dynamic_lamp.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_lamp {
|
||||
|
||||
class DynamicLamp : public output::FloatOutput, public Parented<DynamicLampComponent> {
|
||||
public:
|
||||
DynamicLamp(DynamicLampComponent *parent, DynamicLampIdx lamp) : parent_(parent), lamp_(lamp) {}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
DynamicLampComponent *parent_;
|
||||
DynamicLampIdx lamp_;
|
||||
float state_;
|
||||
};
|
||||
|
||||
} // namespace dynamic_lamp
|
||||
} // namespace esphome
|
@ -1,79 +0,0 @@
|
||||
'''
|
||||
tbd
|
||||
'''
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_ON_TIME,
|
||||
CONF_THEN,
|
||||
CONF_HOUR,
|
||||
CONF_MINUTE,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome import automation
|
||||
from esphome.components.number import Number
|
||||
from esphome.components.switch import Switch
|
||||
from esphome.components.time import RealTimeClock
|
||||
|
||||
MULTI_CONF = True
|
||||
DEPENDENCIES = ["time", "number", "switch"]
|
||||
CODEOWNERS = ["@hostcc"]
|
||||
|
||||
CONF_RTC = 'rtc'
|
||||
CONF_MON = 'mon'
|
||||
CONF_TUE = 'tue'
|
||||
CONF_WED = 'wed'
|
||||
CONF_THU = 'thu'
|
||||
CONF_FRI = 'fri'
|
||||
CONF_SAT = 'sat'
|
||||
CONF_SUN = 'sun'
|
||||
CONF_DISABLED = 'disabled'
|
||||
|
||||
dynamic_on_time_ns = cg.esphome_ns.namespace("dynamic_on_time")
|
||||
DynamicOnTimeComponent = dynamic_on_time_ns.class_(
|
||||
"DynamicOnTime", cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(DynamicOnTimeComponent),
|
||||
cv.Required(CONF_RTC): cv.use_id(RealTimeClock),
|
||||
cv.Required(CONF_HOUR): cv.use_id(Number),
|
||||
cv.Required(CONF_MINUTE): cv.use_id(Number),
|
||||
cv.Required(CONF_MON): cv.use_id(Switch),
|
||||
cv.Required(CONF_TUE): cv.use_id(Switch),
|
||||
cv.Required(CONF_WED): cv.use_id(Switch),
|
||||
cv.Required(CONF_THU): cv.use_id(Switch),
|
||||
cv.Required(CONF_FRI): cv.use_id(Switch),
|
||||
cv.Required(CONF_SAT): cv.use_id(Switch),
|
||||
cv.Required(CONF_SUN): cv.use_id(Switch),
|
||||
cv.Required(CONF_DISABLED): cv.use_id(Switch),
|
||||
cv.Required(CONF_ON_TIME): automation.validate_automation({}),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
'''
|
||||
tbd
|
||||
'''
|
||||
actions = []
|
||||
for conf in config[CONF_ON_TIME]:
|
||||
actions.extend(await automation.build_action_list(
|
||||
conf[CONF_THEN], cg.TemplateArguments(), [])
|
||||
)
|
||||
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
await cg.get_variable(config[CONF_RTC]),
|
||||
await cg.get_variable(config[CONF_HOUR]),
|
||||
await cg.get_variable(config[CONF_MINUTE]),
|
||||
await cg.get_variable(config[CONF_MON]),
|
||||
await cg.get_variable(config[CONF_TUE]),
|
||||
await cg.get_variable(config[CONF_WED]),
|
||||
await cg.get_variable(config[CONF_THU]),
|
||||
await cg.get_variable(config[CONF_FRI]),
|
||||
await cg.get_variable(config[CONF_SAT]),
|
||||
await cg.get_variable(config[CONF_SUN]),
|
||||
await cg.get_variable(config[CONF_DISABLED]),
|
||||
actions,
|
||||
)
|
||||
await cg.register_component(var, config)
|
@ -1,212 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Ilia Sotnikov
|
||||
|
||||
#include "dynamic_on_time.h" // NOLINT(build/include_subdir)
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_on_time {
|
||||
|
||||
static const char *tag = "dynamic_on_time";
|
||||
|
||||
DynamicOnTime::DynamicOnTime(
|
||||
time::RealTimeClock *rtc,
|
||||
number::Number *hour,
|
||||
number::Number *minute,
|
||||
switch_::Switch *mon,
|
||||
switch_::Switch *tue,
|
||||
switch_::Switch *wed,
|
||||
switch_::Switch *thu,
|
||||
switch_::Switch *fri,
|
||||
switch_::Switch *sat,
|
||||
switch_::Switch *sun,
|
||||
switch_::Switch *disabled,
|
||||
std::vector<esphome::Action<> *> actions):
|
||||
rtc_(rtc),
|
||||
hour_(hour), minute_(minute),
|
||||
mon_(mon), tue_(tue), wed_(wed), thu_(thu), fri_(fri), sat_(sat),
|
||||
sun_(sun), disabled_(disabled), actions_(actions) {}
|
||||
|
||||
std::vector<uint8_t> DynamicOnTime::flags_to_days_of_week_(
|
||||
bool mon, bool tue, bool wed, bool thu, bool fri, bool sat, bool sun
|
||||
) {
|
||||
// Numeric representation for days of week (starts from Sun internally)
|
||||
std::vector<uint8_t> days_of_week = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
std::vector<bool> flags = { sun, mon, tue, wed, thu, fri, sat };
|
||||
|
||||
// Translate set of bool flags into vector of corresponding numeric
|
||||
// representation. This uses 'erase-remove' approach (
|
||||
// https://en.cppreference.com/w/cpp/algorithm/remove,
|
||||
// https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom)
|
||||
days_of_week.erase(
|
||||
std::remove_if(
|
||||
std::begin(days_of_week), std::end(days_of_week),
|
||||
[&](uint8_t& arg) { return !flags[&arg - days_of_week.data()]; }),
|
||||
days_of_week.end());
|
||||
|
||||
return days_of_week;
|
||||
}
|
||||
|
||||
void DynamicOnTime::setup() {
|
||||
// Update the configuration initially, ensuring all entities are created
|
||||
// before a callback would be delivered to them
|
||||
this->update_schedule_();
|
||||
|
||||
// Register the cron trigger component
|
||||
App.register_component(this->trigger_);
|
||||
|
||||
// The `Number` and `Switch` has no common base type with
|
||||
// `add_on_state_callback`, and solutions to properly cast to derived
|
||||
// class in single loop over vector of base class instances seemingly imply
|
||||
// more code than just two loops
|
||||
for (number::Number *comp : {this->hour_, this->minute_}) {
|
||||
comp->add_on_state_callback([this](float value) {
|
||||
this->update_schedule_();
|
||||
});
|
||||
}
|
||||
|
||||
for (switch_::Switch *comp : {
|
||||
this->mon_, this->tue_, this->wed_, this->thu_, this->fri_, this->sat_,
|
||||
this->sun_, this->disabled_
|
||||
}) {
|
||||
comp->add_on_state_callback([this](bool value) {
|
||||
this->update_schedule_();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicOnTime::update_schedule_() {
|
||||
// CronTrigger doesn't allow its configuration to be reset programmatically,
|
||||
// so its instance is either created initially, or reinitialized in place if
|
||||
// allocated already
|
||||
if (this->trigger_ != nullptr) {
|
||||
// Use 'placement new' (https://en.cppreference.com/w/cpp/language/new) to
|
||||
// reinitialize existing CronTrigger instance in place
|
||||
this->trigger_->~CronTrigger();
|
||||
new (this->trigger_) time::CronTrigger(this->rtc_);
|
||||
} else {
|
||||
this->trigger_ = new time::CronTrigger(this->rtc_);
|
||||
}
|
||||
|
||||
// (Re)create the automation instance but only if scheduled actions aren't
|
||||
// disabled
|
||||
if (this->automation_ != nullptr) {
|
||||
delete this->automation_;
|
||||
this->automation_ = nullptr;
|
||||
}
|
||||
|
||||
if (!this->disabled_->state) {
|
||||
this->automation_ = new Automation<>(this->trigger_);
|
||||
// Add requested actions to it
|
||||
this->automation_->add_actions(this->actions_);
|
||||
}
|
||||
|
||||
// All remaining logic is active regardless of scheduled actions are
|
||||
// disabled, since callbacks from Switch/Number components being active still
|
||||
// need to be processed otherwise inputs will be lost
|
||||
//
|
||||
// Set trigger to fire on zeroth second of configured time
|
||||
this->trigger_->add_second(0);
|
||||
// Enable all days of months for the schedule
|
||||
for (uint8_t i = 1; i <= 31; i++)
|
||||
this->trigger_->add_day_of_month(i);
|
||||
// Same but for months
|
||||
for (uint8_t i = 1; i <= 12; i++)
|
||||
this->trigger_->add_month(i);
|
||||
// Configure hour/minute of the schedule from corresponding components' state
|
||||
this->trigger_->add_hour(static_cast<uint8_t>(this->hour_->state));
|
||||
this->trigger_->add_minute(static_cast<uint8_t>(this->minute_->state));
|
||||
// Similarly but for days of week translating set of components' state to
|
||||
// vector of numeric representation as `CrontTrigger::add_days_of_week()`
|
||||
// requires
|
||||
this->days_of_week_ = this->flags_to_days_of_week_(
|
||||
this->mon_->state, this->tue_->state, this->wed_->state,
|
||||
this->thu_->state, this->fri_->state, this->sat_->state,
|
||||
this->sun_->state);
|
||||
this->trigger_->add_days_of_week(this->days_of_week_);
|
||||
|
||||
// Initiate updating the cached value for the next schedule
|
||||
this->next_schedule_.reset();
|
||||
|
||||
// Log the configuration
|
||||
this->dump_config();
|
||||
}
|
||||
|
||||
optional<ESPTime> DynamicOnTime::get_next_schedule() {
|
||||
if (this->disabled_->state || this->days_of_week_.empty())
|
||||
return {};
|
||||
|
||||
ESPTime now = this->rtc_->now();
|
||||
|
||||
if (now < this->next_schedule_.value_or(now))
|
||||
return this->next_schedule_;
|
||||
|
||||
ESP_LOGVV(tag, "Non-cached calculation of next schedule");
|
||||
|
||||
// Calculate timestamp for the start of the week with time being hour/time of
|
||||
// the schedule
|
||||
time_t start_of_week = now.timestamp
|
||||
- (now.second + now.hour * 3600 + now.minute * 60 + now.day_of_week * 86400)
|
||||
+ (3600 * static_cast<int>(this->hour_->state)
|
||||
+ 60 * static_cast<int>(this->minute_->state));
|
||||
|
||||
time_t next = 0, first = 0;
|
||||
for (auto next_day : this->days_of_week_) {
|
||||
// Calculate the timestamp for next day in schedule
|
||||
next = start_of_week + 86400 * next_day;
|
||||
// Capture timestamp for the first scheduled day
|
||||
if (!first)
|
||||
first = next;
|
||||
// Exit if timestamp corresponds of later date in the schedule found
|
||||
if (next > now.timestamp)
|
||||
break;
|
||||
}
|
||||
// No later date has been found, use the earlier of scheduled ones plus one
|
||||
// week
|
||||
if (next < now.timestamp)
|
||||
next = first + 7 * 86400;
|
||||
|
||||
return this->next_schedule_ = ESPTime::from_epoch_local(next);
|
||||
}
|
||||
|
||||
void DynamicOnTime::dump_config() {
|
||||
ESP_LOGCONFIG(tag, "Cron trigger details:");
|
||||
ESP_LOGCONFIG(tag, "Disabled: %s", ONOFF(this->disabled_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Hour (source: '%s'): %.0f",
|
||||
this->hour_->get_name().c_str(), this->hour_->state);
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Minute (source: '%s'): %.0f",
|
||||
this->minute_->get_name().c_str(), this->minute_->state);
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Mon (source: '%s'): %s",
|
||||
this->mon_->get_name().c_str(), ONOFF(this->mon_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Tue (source: '%s'): %s",
|
||||
this->tue_->get_name().c_str(), ONOFF(this->tue_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Wed (source: '%s'): %s",
|
||||
this->wed_->get_name().c_str(), ONOFF(this->wed_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Thu (source: '%s'): %s",
|
||||
this->thu_->get_name().c_str(), ONOFF(this->thu_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Fri (source: '%s'): %s",
|
||||
this->fri_->get_name().c_str(), ONOFF(this->fri_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Sat (source: '%s'): %s",
|
||||
this->sat_->get_name().c_str(), ONOFF(this->sat_->state));
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Sun (source: '%s'): %s",
|
||||
this->sun_->get_name().c_str(), ONOFF(this->sun_->state));
|
||||
|
||||
auto schedule = this->get_next_schedule();
|
||||
if (schedule.has_value())
|
||||
ESP_LOGCONFIG(
|
||||
tag, "Next schedule: %s",
|
||||
schedule.value().strftime("%a %H:%M:%S %m/%d/%Y").c_str());
|
||||
}
|
||||
|
||||
} // namespace dynamic_on_time
|
||||
} // namespace esphome
|
@ -1,52 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Ilia Sotnikov
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#include "esphome/components/time/automation.h"
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dynamic_on_time {
|
||||
|
||||
class DynamicOnTime : public Component {
|
||||
public:
|
||||
explicit DynamicOnTime(
|
||||
time::RealTimeClock *, number::Number *, number::Number *,
|
||||
switch_::Switch *, switch_::Switch *, switch_::Switch *, switch_::Switch *,
|
||||
switch_::Switch *, switch_::Switch *, switch_::Switch *, switch_::Switch *,
|
||||
std::vector<esphome::Action<> *>);
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
optional<ESPTime> get_next_schedule();
|
||||
|
||||
protected:
|
||||
time::RealTimeClock *rtc_;
|
||||
number::Number *hour_;
|
||||
number::Number *minute_;
|
||||
switch_::Switch *mon_;
|
||||
switch_::Switch *tue_;
|
||||
switch_::Switch *wed_;
|
||||
switch_::Switch *thu_;
|
||||
switch_::Switch *fri_;
|
||||
switch_::Switch *sat_;
|
||||
switch_::Switch *sun_;
|
||||
switch_::Switch *disabled_;
|
||||
std::vector<esphome::Action<> *> actions_;
|
||||
time::CronTrigger *trigger_{nullptr};
|
||||
Automation<> *automation_{nullptr};
|
||||
std::vector<uint8_t> days_of_week_{};
|
||||
|
||||
std::vector<uint8_t> flags_to_days_of_week_(
|
||||
bool, bool, bool, bool, bool, bool, bool);
|
||||
|
||||
void update_schedule_();
|
||||
optional<ESPTime> next_schedule_{};
|
||||
};
|
||||
|
||||
} // namespace dynamic_on_time
|
||||
} // namespace esphome
|
@ -1,629 +0,0 @@
|
||||
//
|
||||
// FILE: FRAM.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.5.3
|
||||
// DATE: 2018-01-24
|
||||
// PURPOSE: Arduino library for I2C FRAM
|
||||
// URL: https://github.com/RobTillaart/FRAM_I2C
|
||||
//
|
||||
// ESPHome port: https://github.com/sharkydog/esphome-fram
|
||||
|
||||
#include "FRAM.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fram {
|
||||
|
||||
// used for metadata and sleep
|
||||
const uint8_t FRAM_SLAVE_ID_ = 0x7C; // == 0xF8
|
||||
const uint8_t FRAM_SLEEP_CMD = 0x86; //
|
||||
static const char * const TAG = "fram";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM PUBLIC
|
||||
//
|
||||
|
||||
void FRAM::setup()
|
||||
{
|
||||
if (!this->isConnected())
|
||||
{
|
||||
ESP_LOGE(TAG, "Device on address 0x%x not found!", this->address_);
|
||||
this->mark_failed();
|
||||
}
|
||||
else if (!this->getSize())
|
||||
{
|
||||
ESP_LOGW(TAG, "Device on address 0x%x returned 0 size, set size in config!", this->address_);
|
||||
}
|
||||
}
|
||||
|
||||
void FRAM::dump_config()
|
||||
{
|
||||
ESP_LOGCONFIG(TAG, "FRAM:");
|
||||
ESP_LOGCONFIG(TAG, " Address: 0x%x", this->address_);
|
||||
|
||||
bool ok = this->isConnected();
|
||||
|
||||
if (!ok) {
|
||||
ESP_LOGE(TAG, " Device not found!");
|
||||
}
|
||||
|
||||
if (this->_sizeBytes) {
|
||||
ESP_LOGCONFIG(TAG, " Size: %uKiB", this->_sizeBytes / 1024UL);
|
||||
} else if(ok) {
|
||||
ESP_LOGW(TAG, " Size: 0KiB, set size in config!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool FRAM::isConnected()
|
||||
{
|
||||
i2c::ErrorCode err = this->bus_->write(this->address_, nullptr, 0, true);
|
||||
return (err == i2c::ERROR_OK);
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write8(uint16_t memaddr, uint8_t value)
|
||||
{
|
||||
uint8_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write16(uint16_t memaddr, uint16_t value)
|
||||
{
|
||||
uint16_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write32(uint16_t memaddr, uint32_t value)
|
||||
{
|
||||
uint32_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::writeFloat(uint16_t memaddr, float value)
|
||||
{
|
||||
float val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::writeDouble(uint16_t memaddr, double value)
|
||||
{
|
||||
double val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write(uint16_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const int blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remaining
|
||||
if (size > 0)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t FRAM::read8(uint16_t memaddr)
|
||||
{
|
||||
uint8_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM::read16(uint16_t memaddr)
|
||||
{
|
||||
uint16_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::read32(uint16_t memaddr)
|
||||
{
|
||||
uint32_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
float FRAM::readFloat(uint16_t memaddr)
|
||||
{
|
||||
float val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
double FRAM::readDouble(uint16_t memaddr)
|
||||
{
|
||||
double val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void FRAM::read(uint16_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const uint8_t blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_readBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remainder
|
||||
if (size > 0)
|
||||
{
|
||||
this->_readBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int32_t FRAM::readUntil(uint16_t memaddr, char * buf, uint16_t buflen, char separator)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen; length++)
|
||||
{
|
||||
if (buf[length] == separator)
|
||||
{
|
||||
buf[length] = 0; // replace separator => \0 EndChar
|
||||
return length;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM::readLine(uint16_t memaddr, char * buf, uint16_t buflen)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen-1; length++)
|
||||
{
|
||||
if (buf[length] == '\n')
|
||||
{
|
||||
buf[length + 1] = 0; // add \0 EndChar after '\n'
|
||||
return length + 1;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint16_t FRAM::getManufacturerID()
|
||||
{
|
||||
return this->_getMetaData(0);
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM::getProductID()
|
||||
{
|
||||
return this->_getMetaData(1);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: returns the size in kiloBYTE
|
||||
uint16_t FRAM::getSize()
|
||||
{
|
||||
uint16_t density = this->_getMetaData(2);
|
||||
uint16_t size = 0;
|
||||
if (density > 0)
|
||||
{
|
||||
size = (1UL << density);
|
||||
this->_sizeBytes = size * 1024UL;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::getSizeBytes()
|
||||
{
|
||||
return this->_sizeBytes;
|
||||
}
|
||||
|
||||
|
||||
// override to be used when getSize() fails == 0
|
||||
void FRAM::setSizeBytes(uint32_t value)
|
||||
{
|
||||
this->_sizeBytes = value;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::clear(uint8_t value)
|
||||
{
|
||||
uint8_t buffer[16];
|
||||
for (uint8_t i = 0; i < 16; i++) buffer[i] = value;
|
||||
uint32_t start = 0;
|
||||
uint32_t end = this->_sizeBytes;
|
||||
for (uint32_t address = start; address < end; address += 16)
|
||||
{
|
||||
this->_writeBlock(address, buffer, 16);
|
||||
}
|
||||
return end - start;
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL - to be confirmed
|
||||
// page 12 datasheet
|
||||
// command = S 0xF8 A address A S 86 A P (A = Ack from slave )
|
||||
void FRAM::sleep()
|
||||
{
|
||||
uint8_t addr = this->address_ << 1;
|
||||
this->bus_->write(FRAM_SLAVE_ID_, &addr, 1, false);
|
||||
this->bus_->write(FRAM_SLEEP_CMD >> 1, nullptr, 0, true);
|
||||
}
|
||||
|
||||
|
||||
// page 12 datasheet trec <= 400us
|
||||
bool FRAM::wakeup(uint32_t trec)
|
||||
{
|
||||
bool b = this->isConnected(); // wakeup
|
||||
if (trec == 0) return b;
|
||||
// wait recovery time
|
||||
delayMicroseconds(trec);
|
||||
return this->isConnected(); // check recovery OK
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM PROTECTED
|
||||
//
|
||||
|
||||
// metadata is packed as [....MMMM][MMMMDDDD][PPPPPPPP]
|
||||
// M = manufacturerID
|
||||
// D = density => memory size = 2^D KB
|
||||
// P = product ID (together with D)
|
||||
// P part might be proprietary
|
||||
uint16_t FRAM::_getMetaData(uint8_t field)
|
||||
{
|
||||
if (field > 2) return 0;
|
||||
|
||||
uint8_t addr = this->address_ << 1;
|
||||
this->bus_->write(FRAM_SLAVE_ID_, &addr, 1, false);
|
||||
|
||||
uint8_t data[3] = {0,0,0};
|
||||
i2c::ErrorCode err = this->bus_->read(FRAM_SLAVE_ID_, data, 3);
|
||||
if (err != i2c::ERROR_OK) return 0;
|
||||
|
||||
// MANUFACTURER
|
||||
if (field == 0) return (data[0] << 4) + (data[1] >> 4);
|
||||
// PRODUCT ID
|
||||
if (field == 1) return ((data[1] & 0x0F) << 8) + data[2];
|
||||
// DENSITY
|
||||
// Fujitsu data sheet
|
||||
// 3 => MB85RC64 = 64 Kbit.
|
||||
// 5 => MB85RC256
|
||||
// 6 => MB85RC512
|
||||
// 7 => MB85RC1M
|
||||
if (field == 2) return data[1] & 0x0F;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void FRAM::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
|
||||
buff[0].data = maddr;
|
||||
buff[0].len = 2;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(this->address_, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
this->bus_->write(this->address_, maddr, 2, false);
|
||||
this->bus_->read(this->address_, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32 PUBLIC
|
||||
//
|
||||
|
||||
void FRAM32::write8(uint32_t memaddr, uint8_t value)
|
||||
{
|
||||
uint8_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write16(uint32_t memaddr, uint16_t value)
|
||||
{
|
||||
uint16_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write32(uint32_t memaddr, uint32_t value)
|
||||
{
|
||||
uint32_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::writeFloat(uint32_t memaddr, float value)
|
||||
{
|
||||
float val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::writeDouble(uint32_t memaddr, double value)
|
||||
{
|
||||
double val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write(uint32_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const int blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remaining
|
||||
if (size > 0)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t FRAM32::read8(uint32_t memaddr)
|
||||
{
|
||||
uint8_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM32::read16(uint32_t memaddr)
|
||||
{
|
||||
uint16_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM32::read32(uint32_t memaddr)
|
||||
{
|
||||
uint32_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
float FRAM32::readFloat(uint32_t memaddr)
|
||||
{
|
||||
float val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
double FRAM32::readDouble(uint32_t memaddr)
|
||||
{
|
||||
double val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::read(uint32_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const uint8_t blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_readBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remainder
|
||||
if (size > 0)
|
||||
{
|
||||
this->_readBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM32::readUntil(uint32_t memaddr, char * buf, uint16_t buflen, char separator)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen; length++)
|
||||
{
|
||||
if (buf[length] == separator)
|
||||
{
|
||||
buf[length] = 0; // replace separator => \0 EndChar
|
||||
return length;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM32::readLine(uint32_t memaddr, char * buf, uint16_t buflen)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen-1; length++)
|
||||
{
|
||||
if (buf[length] == '\n')
|
||||
{
|
||||
buf[length + 1] = 0; // add \0 EndChar after '\n'
|
||||
return length + 1;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
template <class T> uint32_t FRAM32::writeObject(uint32_t memaddr, T &obj)
|
||||
{
|
||||
this->write(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
};
|
||||
|
||||
|
||||
template <class T> uint32_t FRAM32::readObject(uint32_t memaddr, T &obj)
|
||||
{
|
||||
this->read(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM32::_writeBlock(uint32_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t _addr = this->address_;
|
||||
if (memaddr & 0x00010000) _addr += 0x01;
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
|
||||
buff[0].data = maddr;
|
||||
buff[0].len = 2;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(_addr, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::_readBlock(uint32_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t _addr = this->address_;
|
||||
if (memaddr & 0x00010000) _addr += 0x01;
|
||||
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
this->bus_->write(this->address_, maddr, 2, false);
|
||||
this->bus_->read(_addr, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11
|
||||
//
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM11::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0700) >> 8);
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
buff[0].data = &maddr;
|
||||
buff[0].len = 1;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(DeviceAddrWithPageBits, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM11::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0700) >> 8);
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
this->bus_->write(DeviceAddrWithPageBits, &maddr, 1, false);
|
||||
this->bus_->read(DeviceAddrWithPageBits, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9
|
||||
//
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM9::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0100) >> 8);
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
buff[0].data = &maddr;
|
||||
buff[0].len = 1;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(DeviceAddrWithPageBits, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM9::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0100) >> 8);
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
this->bus_->write(DeviceAddrWithPageBits, &maddr, 1, false);
|
||||
this->bus_->read(DeviceAddrWithPageBits, obj, size);
|
||||
}
|
||||
|
||||
|
||||
} // namespace fram
|
||||
} // namespace esphome
|
||||
|
||||
// -- END OF FILE --
|
@ -1,164 +0,0 @@
|
||||
#pragma once
|
||||
//
|
||||
// FILE: FRAM.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.5.3
|
||||
// DATE: 2018-01-24
|
||||
// PURPOSE: Arduino library for I2C FRAM
|
||||
// URL: https://github.com/RobTillaart/FRAM_I2C
|
||||
//
|
||||
// ESPHome port: https://github.com/sharkydog/esphome-fram
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fram {
|
||||
|
||||
class FRAM : public Component, public i2c::I2CDevice
|
||||
{
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
bool isConnected();
|
||||
|
||||
void write8(uint16_t memaddr, uint8_t value);
|
||||
void write16(uint16_t memaddr, uint16_t value);
|
||||
void write32(uint16_t memaddr, uint32_t value);
|
||||
void writeFloat(uint16_t memaddr, float value);
|
||||
void writeDouble(uint16_t memaddr, double value);
|
||||
void write(uint16_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
uint8_t read8(uint16_t memaddr);
|
||||
uint16_t read16(uint16_t memaddr);
|
||||
uint32_t read32(uint16_t memaddr);
|
||||
float readFloat(uint16_t memaddr);
|
||||
double readDouble(uint16_t memaddr);
|
||||
void read(uint16_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
// Experimental 0.5.1
|
||||
// readUntil returns length 0.. n of the buffer.
|
||||
// readUntil does NOT include the separator character.
|
||||
// readUntil returns -1 if data does not fit into buffer,
|
||||
// => separator not encountered.
|
||||
int32_t readUntil(uint16_t memaddr, char * buf, uint16_t buflen, char separator);
|
||||
// readLine returns length 0.. n of the buffer.
|
||||
// readLine does include '\n' as end character.
|
||||
// readLine returns -1 if data does not fit into buffer.
|
||||
// buffer needs one place for end char '\0'.
|
||||
int32_t readLine(uint16_t memaddr, char * buf, uint16_t buflen);
|
||||
|
||||
template <class T> uint16_t writeObject(uint16_t memaddr, T &obj)
|
||||
{
|
||||
this->write(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
};
|
||||
template <class T> uint16_t readObject(uint16_t memaddr, T &obj)
|
||||
{
|
||||
this->read(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
}
|
||||
|
||||
// meta info
|
||||
// Fujitsu = 0x000A, Ramtron = 0x004
|
||||
uint16_t getManufacturerID();
|
||||
// Proprietary
|
||||
uint16_t getProductID();
|
||||
// Returns size in KILO-BYTE (or 0)
|
||||
// Verify for all manufacturers.
|
||||
uint16_t getSize();
|
||||
// Returns size in BYTE
|
||||
uint32_t getSizeBytes();
|
||||
// override when getSize() fails == 0 (see readme.md)
|
||||
void setSizeBytes(uint32_t value);
|
||||
|
||||
// fills FRAM with value, default 0.
|
||||
uint32_t clear(uint8_t value = 0);
|
||||
|
||||
// 0.3.6
|
||||
void sleep();
|
||||
// trec <= 400us P12
|
||||
bool wakeup(uint32_t trec = 400);
|
||||
|
||||
|
||||
protected:
|
||||
uint32_t _sizeBytes{0};
|
||||
|
||||
uint16_t _getMetaData(uint8_t id);
|
||||
|
||||
// virtual so derived classes FRAM9/11 use their implementation.
|
||||
virtual void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
virtual void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32
|
||||
//
|
||||
|
||||
class FRAM32 : public FRAM
|
||||
{
|
||||
public:
|
||||
void write8(uint32_t memaddr, uint8_t value);
|
||||
void write16(uint32_t memaddr, uint16_t value);
|
||||
void write32(uint32_t memaddr, uint32_t value);
|
||||
void writeFloat(uint32_t memaddr, float value);
|
||||
void writeDouble(uint32_t memaddr, double value);
|
||||
void write(uint32_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
uint8_t read8(uint32_t memaddr);
|
||||
uint16_t read16(uint32_t memaddr);
|
||||
uint32_t read32(uint32_t memaddr);
|
||||
float readFloat(uint32_t memaddr);
|
||||
double readDouble(uint32_t memaddr);
|
||||
void read(uint32_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
// readUntil returns length 0.. n of the buffer.
|
||||
// readUntil returns -1 if data does not fit into buffer,
|
||||
// => separator not encountered.
|
||||
int32_t readUntil(uint32_t memaddr, char * buf, uint16_t buflen, char separator);
|
||||
// buffer needs one place for end char '\0'.
|
||||
int32_t readLine(uint32_t memaddr, char * buf, uint16_t buflen);
|
||||
|
||||
template <class T> uint32_t writeObject(uint32_t memaddr, T &obj);
|
||||
template <class T> uint32_t readObject(uint32_t memaddr, T &obj);
|
||||
|
||||
protected:
|
||||
void _writeBlock(uint32_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint32_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11 for FRAM that use 11 bits addresses - e.g. MB85RC16
|
||||
//
|
||||
|
||||
class FRAM11 : public FRAM
|
||||
{
|
||||
protected:
|
||||
void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9 for FRAM that use 9 bits addresses - e.g. MB85RC04
|
||||
//
|
||||
class FRAM9 : public FRAM
|
||||
{
|
||||
protected:
|
||||
void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
} // namespace fram
|
||||
} // namespace esphome
|
||||
|
||||
// -- END OF FILE --
|
@ -1,64 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import re
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID, CONF_TYPE, CONF_SIZE
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
fram_ns = cg.esphome_ns.namespace("fram")
|
||||
FRAMComponent = fram_ns.class_("FRAM", cg.Component, i2c.I2CDevice)
|
||||
FRAM9Component = fram_ns.class_("FRAM9", FRAMComponent)
|
||||
FRAM11Component = fram_ns.class_("FRAM11", FRAMComponent)
|
||||
FRAM32Component = fram_ns.class_("FRAM32", FRAMComponent)
|
||||
|
||||
def validate_bytes_1024(value):
|
||||
value = cv.string(value).lower()
|
||||
match = re.match(r"^([0-9]+)\s*(\w*)$", value)
|
||||
|
||||
if match is None:
|
||||
raise cv.Invalid(f"Expected number of bytes with unit, got {value}")
|
||||
|
||||
SUFF_LIST = ["B","KB","KiB","MB","MiB"]
|
||||
SUFF = {
|
||||
"": 1,
|
||||
"b": 1,
|
||||
"kb": 1000,
|
||||
"kib": 1024,
|
||||
"mb": pow(1000,2),
|
||||
"mib": pow(1024,2)
|
||||
}
|
||||
|
||||
if match.group(2) not in SUFF:
|
||||
raise cv.Invalid(f"Invalid metric suffix {match.group(2)}, valid: {SUFF_LIST}")
|
||||
|
||||
return int(int(match.group(1)) * SUFF[match.group(2)])
|
||||
|
||||
|
||||
FRAM_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_SIZE): validate_bytes_1024
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x50))
|
||||
|
||||
CONFIG_SCHEMA = cv.typed_schema({
|
||||
"FRAM": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAMComponent)
|
||||
}),
|
||||
"FRAM9": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM9Component)
|
||||
}),
|
||||
"FRAM11": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM11Component)
|
||||
}),
|
||||
"FRAM32": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM32Component)
|
||||
})
|
||||
}, key=CONF_TYPE, default_type="FRAM", upper=True)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_SIZE in config:
|
||||
cg.add(var.setSizeBytes(config[CONF_SIZE]))
|
@ -100,7 +100,7 @@ class Mcp4461Component : public Component, public i2c::I2CDevice {
|
||||
/// @param[wiper] wiper for which terminal shall be initialized disabled
|
||||
/// @param[terminal] terminal to disable, one of { 'a', 'b', 'w', 'h' }
|
||||
void initialize_terminal_disabled(Mcp4461WiperIdx wiper, char terminal);
|
||||
|
||||
|
||||
/// @brief available/required status codes
|
||||
enum ErrorCode {
|
||||
MCP4461_STATUS_OK = 0, // CMD completed successfully
|
||||
@ -122,70 +122,68 @@ class Mcp4461Component : public Component, public i2c::I2CDevice {
|
||||
/// @brief update write protection status of device
|
||||
void update_write_protection_status_();
|
||||
/// @brief fetch wiper address for given wiper
|
||||
/// @param[in] wiper - Wiper to fetch address for, int in range 0-7
|
||||
/// @return wiper - Address from Mcp4461Addresses
|
||||
/// @param[wiper] wiper to fetch address for, int in range 0-7
|
||||
/// @return wiper address from Mcp4461Addresses
|
||||
uint8_t get_wiper_address_(uint8_t wiper);
|
||||
/// @brief internal i2c function to read given wiper value
|
||||
/// @return uint16_t - Value in range 0-256 representing current wiper state/level
|
||||
/// @return uint16_t value in range 0-256 representing current wiper state/level
|
||||
uint16_t read_wiper_level_(uint8_t wiper);
|
||||
/// @brief fetch device status register values
|
||||
/// @return uint8_t - Status register value - see datasheet for bit values
|
||||
/// @return uint8_t status register value - see datasheet for bit values
|
||||
uint8_t get_status_register_();
|
||||
/// @brief read current level/state of given wiper with validation checks
|
||||
/// @return uint16_t - Value in range 0-256 representing current wiper state/level
|
||||
/// @return uint16_t value in range 0-256 representing current wiper state/level
|
||||
uint16_t get_wiper_level_(Mcp4461WiperIdx wiper);
|
||||
/// @brief set level/state of given wiper
|
||||
/// @return bool - True on success, false on error/warning
|
||||
/// @return bool - true on success, false on error/warning
|
||||
bool set_wiper_level_(Mcp4461WiperIdx wiper, uint16_t value);
|
||||
/// @brief update current level/state of given wiper
|
||||
/// @return bool - True on success, false on error/warning
|
||||
/// @return bool - true on success, false on error/warning
|
||||
bool update_wiper_level_(Mcp4461WiperIdx wiper);
|
||||
/// @brief enable given wiper
|
||||
/// @param[in] wiper - The wiper to enable
|
||||
/// @param[in] wiper The wiper to enable
|
||||
void enable_wiper_(Mcp4461WiperIdx wiper);
|
||||
/// @brief disable given wiper
|
||||
/// @param[in] wiper - The wiper to disable
|
||||
/// @param[in] wiper The wiper to disable
|
||||
void disable_wiper_(Mcp4461WiperIdx wiper);
|
||||
/// @brief increase given wiper
|
||||
/// @param[in] wiper - Wiper to increase
|
||||
/// @return bool - True on success, false on error/warning
|
||||
/// @param[wiper] wiper to increase
|
||||
/// @return bool - true on success, false on error/warning
|
||||
bool increase_wiper_(Mcp4461WiperIdx wiper);
|
||||
/// @brief increase given wiper
|
||||
/// @param[in] wiper - Wiper to decrease
|
||||
/// @return bool - True on success, false on error/warning
|
||||
/// @param[wiper] wiper to increase
|
||||
/// @return bool - true on success, false on error/warning
|
||||
bool decrease_wiper_(Mcp4461WiperIdx wiper);
|
||||
/// @brief enable terminal of wiper
|
||||
/// @param[in] wiper - The desired wiper for which the terminal shall be enabled
|
||||
/// @param[in] terminal - The terminal that is to be enabled for given wiper
|
||||
/// @param[wiper] desired wiper for which the terminal shall be enabled
|
||||
void enable_terminal_(Mcp4461WiperIdx wiper, char terminal);
|
||||
/// @brief disable terminal of wiper
|
||||
/// @param[in] wiper - The desired wiper for which the terminal shall be disabled
|
||||
/// @param[in] terminal - The terminal that is to be enabled for given wiper
|
||||
/// @param[wiper] desired wiper for which the terminal shall be disabled
|
||||
void disable_terminal_(Mcp4461WiperIdx, char terminal);
|
||||
/// @brief check if device is still busy writing to eeprom
|
||||
/// @return bool - true if device is currently writing to eeprom
|
||||
bool is_writing_();
|
||||
/// @brief wait until timeout if device is busy
|
||||
/// @param[in] wait_if_not_ready - Set to true to wait until timeout again, if previous write timed out already
|
||||
/// @param[wait_if_not_ready] set to true to wait until timeout again, if previous write timed out already
|
||||
/// @return bool - true if device eeprom still busy, false if rdy for write to nonvolatile wiper/eeprom
|
||||
bool is_eeprom_ready_for_writing_(bool wait_if_not_ready);
|
||||
/// @brief set wiper level
|
||||
/// @param[in] wiper - The wiper for which the new state shall be set
|
||||
/// @param[in] value - The int value in range 0-256 the wiper shall be set to
|
||||
/// @param[wiper] wiper for which the new state shall be set
|
||||
/// @param[value] the int value in range 0-256 the wiper shall be set to
|
||||
void write_wiper_level_(uint8_t wiper, uint16_t value);
|
||||
/// @brief internal i2c write function
|
||||
/// @return bool - True write successful, false if not
|
||||
/// @return bool - true write successful, false if not
|
||||
bool mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile = false);
|
||||
/// @brief calculate correct terminal register values
|
||||
/// @return uint8_t - Calculated terminal register value for current internal terminal states
|
||||
/// @return uint8_t - calculated terminal register value for current internal terminal states
|
||||
uint8_t calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector);
|
||||
/// @brief internal function to update terminal registers
|
||||
void update_terminal_register_(Mcp4461TerminalIdx terminal_connector);
|
||||
/// @brief internal function to get terminal register values
|
||||
/// @return uint8_t - Get terminal register value of specified terminal
|
||||
/// /// @return uint8_t - get terminal register value of specified terminal
|
||||
uint8_t get_terminal_register_(Mcp4461TerminalIdx terminal_connector);
|
||||
/// @brief internal function to set terminal registers
|
||||
/// @return bool - True if write successful, false if not
|
||||
/// @return bool - true if write successful, false if not
|
||||
bool set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data);
|
||||
|
||||
WiperState reg_[8];
|
||||
|
@ -4,7 +4,7 @@ from esphome.components import output
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_INITIAL_VALUE
|
||||
from .. import Mcp4461Component, CONF_MCP4461_ID, mcp4461_ns
|
||||
|
||||
DEPENDENCIES = ["mcp4461", "output"]
|
||||
DEPENDENCIES = ["mcp4461"]
|
||||
|
||||
Mcp4461Wiper = mcp4461_ns.class_(
|
||||
"Mcp4461Wiper", output.FloatOutput, cg.Parented.template(Mcp4461Component)
|
||||
|
Loading…
x
Reference in New Issue
Block a user