mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Merge branch 'dev' of https://github.com/esphome/esphome into dev
This commit is contained in:
commit
cf6205f9b8
22 changed files with 160 additions and 44 deletions
1
.github/workflows/ci-docker.yml
vendored
1
.github/workflows/ci-docker.yml
vendored
|
@ -18,6 +18,7 @@ jobs:
|
||||||
name: Build docker containers
|
name: Build docker containers
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [amd64, armv7, aarch64]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type: ["hassio", "docker"]
|
build_type: ["hassio", "docker"]
|
||||||
|
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -44,6 +44,7 @@ jobs:
|
||||||
container: esphome/esphome-lint:latest
|
container: esphome/esphome-lint:latest
|
||||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
split: [1, 2, 3, 4]
|
split: [1, 2, 3, 4]
|
||||||
steps:
|
steps:
|
||||||
|
@ -107,6 +108,7 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
test:
|
test:
|
||||||
- test1
|
- test1
|
||||||
|
|
2
.github/workflows/release-dev.yml
vendored
2
.github/workflows/release-dev.yml
vendored
|
@ -41,6 +41,7 @@ jobs:
|
||||||
container: esphome/esphome-lint:latest
|
container: esphome/esphome-lint:latest
|
||||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
split: [1, 2, 3, 4]
|
split: [1, 2, 3, 4]
|
||||||
steps:
|
steps:
|
||||||
|
@ -104,6 +105,7 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
test:
|
test:
|
||||||
- test1
|
- test1
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -40,6 +40,7 @@ jobs:
|
||||||
container: esphome/esphome-lint:latest
|
container: esphome/esphome-lint:latest
|
||||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
split: [1, 2, 3, 4]
|
split: [1, 2, 3, 4]
|
||||||
steps:
|
steps:
|
||||||
|
@ -103,6 +104,7 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
test:
|
test:
|
||||||
- test1
|
- test1
|
||||||
|
|
|
@ -12,8 +12,10 @@ void DaikinClimate::transmit_state() {
|
||||||
0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00};
|
0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
remote_state[21] = this->operation_mode_();
|
remote_state[21] = this->operation_mode_();
|
||||||
remote_state[24] = this->fan_speed_();
|
|
||||||
remote_state[22] = this->temperature_();
|
remote_state[22] = this->temperature_();
|
||||||
|
uint16_t fan_speed = this->fan_speed_();
|
||||||
|
remote_state[24] = fan_speed >> 8;
|
||||||
|
remote_state[25] = fan_speed & 0xff;
|
||||||
|
|
||||||
// Calculate checksum
|
// Calculate checksum
|
||||||
for (int i = 16; i < 34; i++) {
|
for (int i = 16; i < 34; i++) {
|
||||||
|
@ -90,25 +92,38 @@ uint8_t DaikinClimate::operation_mode_() {
|
||||||
return operating_mode;
|
return operating_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t DaikinClimate::fan_speed_() {
|
uint16_t DaikinClimate::fan_speed_() {
|
||||||
uint8_t fan_speed;
|
uint16_t fan_speed;
|
||||||
switch (this->fan_mode) {
|
switch (this->fan_mode) {
|
||||||
case climate::CLIMATE_FAN_LOW:
|
case climate::CLIMATE_FAN_LOW:
|
||||||
fan_speed = DAIKIN_FAN_1;
|
fan_speed = DAIKIN_FAN_1 << 8;
|
||||||
break;
|
break;
|
||||||
case climate::CLIMATE_FAN_MEDIUM:
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
fan_speed = DAIKIN_FAN_3;
|
fan_speed = DAIKIN_FAN_3 << 8;
|
||||||
break;
|
break;
|
||||||
case climate::CLIMATE_FAN_HIGH:
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
fan_speed = DAIKIN_FAN_5;
|
fan_speed = DAIKIN_FAN_5 << 8;
|
||||||
break;
|
break;
|
||||||
case climate::CLIMATE_FAN_AUTO:
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
default:
|
default:
|
||||||
fan_speed = DAIKIN_FAN_AUTO;
|
fan_speed = DAIKIN_FAN_AUTO << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If swing is enabled switch first 4 bits to 1111
|
// If swing is enabled switch first 4 bits to 1111
|
||||||
return this->swing_mode == climate::CLIMATE_SWING_VERTICAL ? fan_speed | 0xF : fan_speed;
|
switch (this->swing_mode) {
|
||||||
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
|
fan_speed |= 0x0F00;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
fan_speed |= 0x000F;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_BOTH:
|
||||||
|
fan_speed |= 0x0F0F;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fan_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t DaikinClimate::temperature_() {
|
uint8_t DaikinClimate::temperature_() {
|
||||||
|
@ -159,13 +174,19 @@ bool DaikinClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||||
this->target_temperature = temperature >> 1;
|
this->target_temperature = temperature >> 1;
|
||||||
}
|
}
|
||||||
uint8_t fan_mode = frame[8];
|
uint8_t fan_mode = frame[8];
|
||||||
if (fan_mode & 0xF)
|
uint8_t swing_mode = frame[9];
|
||||||
|
if (fan_mode & 0xF && swing_mode & 0xF)
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||||
|
else if (fan_mode & 0xF)
|
||||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||||
|
else if (swing_mode & 0xF)
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||||
else
|
else
|
||||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||||
switch (fan_mode & 0xF0) {
|
switch (fan_mode & 0xF0) {
|
||||||
case DAIKIN_FAN_1:
|
case DAIKIN_FAN_1:
|
||||||
case DAIKIN_FAN_2:
|
case DAIKIN_FAN_2:
|
||||||
|
case DAIKIN_FAN_SILENT:
|
||||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||||
break;
|
break;
|
||||||
case DAIKIN_FAN_3:
|
case DAIKIN_FAN_3:
|
||||||
|
|
|
@ -21,6 +21,7 @@ const uint8_t DAIKIN_MODE_ON = 0x01;
|
||||||
|
|
||||||
// Fan Speed
|
// Fan Speed
|
||||||
const uint8_t DAIKIN_FAN_AUTO = 0xA0;
|
const uint8_t DAIKIN_FAN_AUTO = 0xA0;
|
||||||
|
const uint8_t DAIKIN_FAN_SILENT = 0xB0;
|
||||||
const uint8_t DAIKIN_FAN_1 = 0x30;
|
const uint8_t DAIKIN_FAN_1 = 0x30;
|
||||||
const uint8_t DAIKIN_FAN_2 = 0x40;
|
const uint8_t DAIKIN_FAN_2 = 0x40;
|
||||||
const uint8_t DAIKIN_FAN_3 = 0x50;
|
const uint8_t DAIKIN_FAN_3 = 0x50;
|
||||||
|
@ -46,13 +47,14 @@ class DaikinClimate : public climate_ir::ClimateIR {
|
||||||
DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX, 1.0f, true, true,
|
DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX, 1.0f, true, true,
|
||||||
std::vector<climate::ClimateFanMode>{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW,
|
std::vector<climate::ClimateFanMode>{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW,
|
||||||
climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
|
climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
|
||||||
std::vector<climate::ClimateSwingMode>{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
std::vector<climate::ClimateSwingMode>{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
|
||||||
|
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Transmit via IR the state of this climate controller.
|
// Transmit via IR the state of this climate controller.
|
||||||
void transmit_state() override;
|
void transmit_state() override;
|
||||||
uint8_t operation_mode_();
|
uint8_t operation_mode_();
|
||||||
uint8_t fan_speed_();
|
uint16_t fan_speed_();
|
||||||
uint8_t temperature_();
|
uint8_t temperature_();
|
||||||
// Handle received IR Buffer
|
// Handle received IR Buffer
|
||||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||||
|
|
|
@ -299,7 +299,7 @@ void DisplayBuffer::printf(int x, int y, Font *font, TextAlign align, const char
|
||||||
void DisplayBuffer::printf(int x, int y, Font *font, const char *format, ...) {
|
void DisplayBuffer::printf(int x, int y, Font *font, const char *format, ...) {
|
||||||
va_list arg;
|
va_list arg;
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
this->vprintf_(x, y, font, COLOR_ON, TextAlign::CENTER_LEFT, format, arg);
|
this->vprintf_(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, arg);
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
}
|
}
|
||||||
void DisplayBuffer::set_writer(display_writer_t &&writer) { this->writer_ = writer; }
|
void DisplayBuffer::set_writer(display_writer_t &&writer) { this->writer_ = writer; }
|
||||||
|
|
|
@ -36,7 +36,10 @@ const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01;
|
||||||
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
|
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
|
||||||
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
|
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
|
||||||
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
|
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
|
||||||
const uint8_t FUJITSU_GENERAL_SWING_MASK_BYTE10 = 0b00010000;
|
const uint8_t FUJITSU_GENERAL_SWING_NONE_BYTE10 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 = 0x01;
|
||||||
|
const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 = 0x02;
|
||||||
|
const uint8_t FUJITSU_GENERAL_SWING_BOTH_BYTE10 = 0x03;
|
||||||
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
|
||||||
|
|
||||||
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
|
||||||
|
@ -74,7 +77,12 @@ const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
|
||||||
|
|
||||||
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
|
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
|
||||||
|
|
||||||
FujitsuGeneralClimate::FujitsuGeneralClimate() : ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1) {}
|
FujitsuGeneralClimate::FujitsuGeneralClimate()
|
||||||
|
: ClimateIR(
|
||||||
|
FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true,
|
||||||
|
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
|
||||||
|
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
|
||||||
|
climate::CLIMATE_SWING_BOTH}) {}
|
||||||
|
|
||||||
void FujitsuGeneralClimate::transmit_state() {
|
void FujitsuGeneralClimate::transmit_state() {
|
||||||
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||||
|
@ -101,8 +109,8 @@ void FujitsuGeneralClimate::transmit_state() {
|
||||||
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
|
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
|
||||||
|
|
||||||
// Set temperature
|
// Set temperature
|
||||||
uint8_t safecelsius = std::max((uint8_t) this->target_temperature, FUJITSU_GENERAL_TEMP_MIN);
|
auto safecelsius =
|
||||||
safecelsius = std::min(safecelsius, FUJITSU_GENERAL_TEMP_MAX);
|
(uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX));
|
||||||
remote_state[8] = (byte) safecelsius - 16;
|
remote_state[8] = (byte) safecelsius - 16;
|
||||||
remote_state[8] = remote_state[8] << 4;
|
remote_state[8] = remote_state[8] << 4;
|
||||||
|
|
||||||
|
@ -119,18 +127,52 @@ void FujitsuGeneralClimate::transmit_state() {
|
||||||
case climate::CLIMATE_MODE_HEAT:
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
|
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
|
||||||
break;
|
break;
|
||||||
|
case climate::CLIMATE_MODE_DRY:
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_MODE_DRY_BYTE9;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_MODE_FAN_BYTE9;
|
||||||
|
break;
|
||||||
case climate::CLIMATE_MODE_AUTO:
|
case climate::CLIMATE_MODE_AUTO:
|
||||||
default:
|
default:
|
||||||
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
|
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
|
||||||
break;
|
break;
|
||||||
// TODO: CLIMATE_MODE_FAN_ONLY, CLIMATE_MODE_DRY, CLIMATE_MODE_10C are missing in esphome
|
// TODO: CLIMATE_MODE_10C are missing in esphome
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: missing support for fan speed
|
// Set fan
|
||||||
|
switch (this->fan_mode) {
|
||||||
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
|
remote_state[10] = FUJITSU_GENERAL_FAN_HIGH_BYTE10;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
|
remote_state[10] = FUJITSU_GENERAL_FAN_MEDIUM_BYTE10;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_LOW:
|
||||||
|
remote_state[10] = FUJITSU_GENERAL_FAN_LOW_BYTE10;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
|
default:
|
||||||
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
|
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: missing support for swing
|
// Set swing
|
||||||
// remote_state[10] = (byte) remote_state[10] | FUJITSU_GENERAL_SWING_MASK_BYTE10;
|
switch (this->swing_mode) {
|
||||||
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
|
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 << 4);
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 << 4);
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_BOTH:
|
||||||
|
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_BOTH_BYTE10 << 4);
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_OFF:
|
||||||
|
default:
|
||||||
|
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_NONE_BYTE10 << 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: missing support for outdoor unit low noise
|
// TODO: missing support for outdoor unit low noise
|
||||||
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
|
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AQICalculator : public AbstractAQICalculator {
|
||||||
|
|
||||||
int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 51}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
|
int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 51}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
|
||||||
|
|
||||||
int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 12}, {13, 45}, {36, 55}, {56, 150}, {151, 250}, {251, 500}};
|
int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 12}, {13, 35}, {36, 55}, {56, 150}, {151, 250}, {251, 500}};
|
||||||
|
|
||||||
int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254},
|
int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254},
|
||||||
{255, 354}, {355, 424}, {425, 604}};
|
{255, 354}, {355, 424}, {425, 604}};
|
||||||
|
|
|
@ -102,17 +102,18 @@ class LightTurnOnTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
LightTurnOnTrigger(LightState *a_light) {
|
LightTurnOnTrigger(LightState *a_light) {
|
||||||
a_light->add_new_remote_values_callback([this, a_light]() {
|
a_light->add_new_remote_values_callback([this, a_light]() {
|
||||||
auto is_on = a_light->current_values.is_on();
|
// using the remote value because of transitions we need to trigger as early as possible
|
||||||
|
auto is_on = a_light->remote_values.is_on();
|
||||||
// only trigger when going from off to on
|
// only trigger when going from off to on
|
||||||
auto should_trigger = is_on && !last_on_;
|
auto should_trigger = is_on && !this->last_on_;
|
||||||
// Set new state immediately so that trigger() doesn't devolve
|
// Set new state immediately so that trigger() doesn't devolve
|
||||||
// into infinite loop
|
// into infinite loop
|
||||||
last_on_ = is_on;
|
this->last_on_ = is_on;
|
||||||
if (should_trigger) {
|
if (should_trigger) {
|
||||||
this->trigger();
|
this->trigger();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
last_on_ = a_light->current_values.is_on();
|
this->last_on_ = a_light->current_values.is_on();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -122,22 +123,14 @@ class LightTurnOnTrigger : public Trigger<> {
|
||||||
class LightTurnOffTrigger : public Trigger<> {
|
class LightTurnOffTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
LightTurnOffTrigger(LightState *a_light) {
|
LightTurnOffTrigger(LightState *a_light) {
|
||||||
a_light->add_new_remote_values_callback([this, a_light]() {
|
a_light->add_new_target_state_reached_callback([this, a_light]() {
|
||||||
auto is_on = a_light->current_values.is_on();
|
auto is_on = a_light->current_values.is_on();
|
||||||
// only trigger when going from on to off
|
// only trigger when going from on to off
|
||||||
auto should_trigger = !is_on && last_on_;
|
if (!is_on) {
|
||||||
// Set new state immediately so that trigger() doesn't devolve
|
|
||||||
// into infinite loop
|
|
||||||
last_on_ = is_on;
|
|
||||||
if (should_trigger) {
|
|
||||||
this->trigger();
|
this->trigger();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
last_on_ = a_light->current_values.is_on();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
bool last_on_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
||||||
|
|
|
@ -145,6 +145,7 @@ void LightState::loop() {
|
||||||
if (this->transformer_ != nullptr) {
|
if (this->transformer_ != nullptr) {
|
||||||
if (this->transformer_->is_finished()) {
|
if (this->transformer_->is_finished()) {
|
||||||
this->remote_values = this->current_values = this->transformer_->get_end_values();
|
this->remote_values = this->current_values = this->transformer_->get_end_values();
|
||||||
|
this->target_state_reached_callback_.call();
|
||||||
if (this->transformer_->publish_at_end())
|
if (this->transformer_->publish_at_end())
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
this->transformer_ = nullptr;
|
this->transformer_ = nullptr;
|
||||||
|
@ -336,6 +337,9 @@ void LightCall::perform() {
|
||||||
this->parent_->set_immediately_(v, this->publish_);
|
this->parent_->set_immediately_(v, this->publish_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this->has_transition_()) {
|
||||||
|
this->parent_->target_state_reached_callback_.call();
|
||||||
|
}
|
||||||
if (this->publish_) {
|
if (this->publish_) {
|
||||||
this->parent_->publish_state();
|
this->parent_->publish_state();
|
||||||
}
|
}
|
||||||
|
@ -752,6 +756,10 @@ void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bo
|
||||||
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
||||||
this->remote_values_callback_.add(std::move(send_callback));
|
this->remote_values_callback_.add(std::move(send_callback));
|
||||||
}
|
}
|
||||||
|
void LightState::add_new_target_state_reached_callback(std::function<void()> &&send_callback) {
|
||||||
|
this->target_state_reached_callback_.add(std::move(send_callback));
|
||||||
|
}
|
||||||
|
|
||||||
LightEffect *LightState::get_active_effect_() {
|
LightEffect *LightState::get_active_effect_() {
|
||||||
if (this->active_effect_index_ == 0)
|
if (this->active_effect_index_ == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -242,6 +242,13 @@ class LightState : public Nameable, public Component {
|
||||||
*/
|
*/
|
||||||
void add_new_remote_values_callback(std::function<void()> &&send_callback);
|
void add_new_remote_values_callback(std::function<void()> &&send_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback is called once the state of current_values and remote_values are equal
|
||||||
|
*
|
||||||
|
* @param send_callback
|
||||||
|
*/
|
||||||
|
void add_new_target_state_reached_callback(std::function<void()> &&send_callback);
|
||||||
|
|
||||||
/// Return whether the light has any effects that meet the trait requirements.
|
/// Return whether the light has any effects that meet the trait requirements.
|
||||||
bool supports_effects();
|
bool supports_effects();
|
||||||
|
|
||||||
|
@ -318,6 +325,12 @@ class LightState : public Nameable, public Component {
|
||||||
* starting with the beginning of the transition.
|
* starting with the beginning of the transition.
|
||||||
*/
|
*/
|
||||||
CallbackManager<void()> remote_values_callback_{};
|
CallbackManager<void()> remote_values_callback_{};
|
||||||
|
|
||||||
|
/** Callback to call when the state of current_values and remote_values are equal
|
||||||
|
* This should be called once the state of current_values changed and equals the state of remote_values
|
||||||
|
*/
|
||||||
|
CallbackManager<void()> target_state_reached_callback_{};
|
||||||
|
|
||||||
LightOutput *output_; ///< Store the output to allow effects to have more access.
|
LightOutput *output_; ///< Store the output to allow effects to have more access.
|
||||||
/// Whether the light value should be written in the next cycle.
|
/// Whether the light value should be written in the next cycle.
|
||||||
bool next_write_{true};
|
bool next_write_{true};
|
||||||
|
|
|
@ -11,6 +11,7 @@ CONF_SCROLL_DWELL = 'scroll_dwell'
|
||||||
CONF_SCROLL_DELAY = 'scroll_delay'
|
CONF_SCROLL_DELAY = 'scroll_delay'
|
||||||
CONF_SCROLL_ENABLE = 'scroll_enable'
|
CONF_SCROLL_ENABLE = 'scroll_enable'
|
||||||
CONF_SCROLL_MODE = 'scroll_mode'
|
CONF_SCROLL_MODE = 'scroll_mode'
|
||||||
|
CONF_REVERSE_ENABLE = 'reverse_enable'
|
||||||
|
|
||||||
SCROLL_MODES = {
|
SCROLL_MODES = {
|
||||||
'CONTINUOUS': 0,
|
'CONTINUOUS': 0,
|
||||||
|
@ -39,6 +40,7 @@ CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
|
||||||
cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_SCROLL_DELAY, default='1000ms'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_SCROLL_DELAY, default='1000ms'): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_SCROLL_DWELL, default='1000ms'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_SCROLL_DWELL, default='1000ms'): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
|
||||||
}).extend(cv.polling_component_schema('500ms')).extend(spi.spi_device_schema(cs_pin_required=True))
|
}).extend(cv.polling_component_schema('500ms')).extend(spi.spi_device_schema(cs_pin_required=True))
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +58,7 @@ def to_code(config):
|
||||||
cg.add(var.set_scroll_delay(config[CONF_SCROLL_DELAY]))
|
cg.add(var.set_scroll_delay(config[CONF_SCROLL_DELAY]))
|
||||||
cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE]))
|
cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE]))
|
||||||
cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE]))
|
cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE]))
|
||||||
|
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
|
||||||
|
|
||||||
if CONF_LAMBDA in config:
|
if CONF_LAMBDA in config:
|
||||||
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
|
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
|
||||||
|
|
|
@ -108,8 +108,12 @@ void MAX7219Component::display() {
|
||||||
// Send the data to the chip
|
// Send the data to the chip
|
||||||
for (uint8_t i = 0; i < this->num_chips_; i++) {
|
for (uint8_t i = 0; i < this->num_chips_; i++) {
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
if (this->reverse_) {
|
||||||
|
pixels[j] = this->max_displaybuffer_[(this->num_chips_ - i - 1) * 8 + j];
|
||||||
|
} else {
|
||||||
pixels[j] = this->max_displaybuffer_[i * 8 + j];
|
pixels[j] = this->max_displaybuffer_[i * 8 + j];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this->send64pixels(i, pixels);
|
this->send64pixels(i, pixels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +233,7 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||||
b = pixels[col];
|
b = pixels[col];
|
||||||
} else if (this->orientation_ == 2) {
|
} else if (this->orientation_ == 2) {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
b |= ((pixels[i] >> (7 - col)) << (7 - i));
|
b |= ((pixels[i] >> (7 - col)) & 1) << i;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b = pixels[7 - col];
|
b = pixels[7 - col];
|
||||||
|
|
|
@ -52,6 +52,7 @@ class MAX7219Component : public PollingComponent,
|
||||||
void set_scroll_delay(uint16_t delay) { this->scroll_delay_ = delay; };
|
void set_scroll_delay(uint16_t delay) { this->scroll_delay_ = delay; };
|
||||||
void set_scroll(bool on_off) { this->scroll_ = on_off; };
|
void set_scroll(bool on_off) { this->scroll_ = on_off; };
|
||||||
void set_scroll_mode(uint8_t mode) { this->scroll_mode_ = mode; };
|
void set_scroll_mode(uint8_t mode) { this->scroll_mode_ = mode; };
|
||||||
|
void set_reverse(bool on_off) { this->reverse_ = on_off; };
|
||||||
|
|
||||||
void send_char(byte chip, byte data);
|
void send_char(byte chip, byte data);
|
||||||
void send64pixels(byte chip, const byte pixels[8]);
|
void send64pixels(byte chip, const byte pixels[8]);
|
||||||
|
@ -87,6 +88,7 @@ class MAX7219Component : public PollingComponent,
|
||||||
uint8_t intensity_; /// Intensity of the display from 0 to 15 (most)
|
uint8_t intensity_; /// Intensity of the display from 0 to 15 (most)
|
||||||
uint8_t num_chips_;
|
uint8_t num_chips_;
|
||||||
bool scroll_;
|
bool scroll_;
|
||||||
|
bool reverse_;
|
||||||
bool update_{false};
|
bool update_{false};
|
||||||
uint16_t scroll_speed_;
|
uint16_t scroll_speed_;
|
||||||
uint16_t scroll_delay_;
|
uint16_t scroll_delay_;
|
||||||
|
|
|
@ -29,10 +29,9 @@ void FloatOutput::set_level(float state) {
|
||||||
this->power_.unrequest();
|
this->power_.unrequest();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float adjusted_value = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
|
|
||||||
if (this->is_inverted())
|
if (this->is_inverted())
|
||||||
adjusted_value = 1.0f - adjusted_value;
|
state = 1.0f - state;
|
||||||
|
float adjusted_value = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
|
||||||
this->write_state(adjusted_value);
|
this->write_state(adjusted_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,11 @@ import tzlocal
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \
|
from esphome.const import CONF_ID, CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \
|
||||||
CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID, \
|
CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID, \
|
||||||
CONF_AT, CONF_SECOND, CONF_HOUR, CONF_MINUTE
|
CONF_AT, CONF_SECOND, CONF_HOUR, CONF_MINUTE
|
||||||
from esphome.core import coroutine, coroutine_with_priority
|
from esphome.core import coroutine, coroutine_with_priority
|
||||||
|
from esphome.automation import Condition
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ time_ns = cg.esphome_ns.namespace('time')
|
||||||
RealTimeClock = time_ns.class_('RealTimeClock', cg.Component)
|
RealTimeClock = time_ns.class_('RealTimeClock', cg.Component)
|
||||||
CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component)
|
CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component)
|
||||||
ESPTime = time_ns.struct('ESPTime')
|
ESPTime = time_ns.struct('ESPTime')
|
||||||
|
TimeHasTimeCondition = time_ns.class_('TimeHasTimeCondition', Condition)
|
||||||
|
|
||||||
|
|
||||||
def _tz_timedelta(td):
|
def _tz_timedelta(td):
|
||||||
|
@ -328,3 +330,11 @@ def register_time(time_var, config):
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
cg.add_define('USE_TIME')
|
cg.add_define('USE_TIME')
|
||||||
cg.add_global(time_ns.using)
|
cg.add_global(time_ns.using)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition('time.has_time', TimeHasTimeCondition, cv.Schema({
|
||||||
|
cv.GenerateID(): cv.use_id(RealTimeClock),
|
||||||
|
}))
|
||||||
|
def time_has_time_to_code(config, condition_id, template_arg, args):
|
||||||
|
paren = yield cg.get_variable(config[CONF_ID])
|
||||||
|
yield cg.new_Pvariable(condition_id, template_arg, paren)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
@ -133,5 +134,14 @@ class RealTimeClock : public Component {
|
||||||
std::string timezone_{};
|
std::string timezone_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class TimeHasTimeCondition : public Condition<Ts...> {
|
||||||
|
public:
|
||||||
|
TimeHasTimeCondition(RealTimeClock *parent) : parent_(parent) {}
|
||||||
|
bool check(Ts... x) override { return this->parent_->now().is_valid(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RealTimeClock *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace time
|
} // namespace time
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -31,6 +31,7 @@ void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &kl
|
||||||
stream->print("</td><td></td><td>");
|
stream->print("</td><td></td><td>");
|
||||||
stream->print(action.c_str());
|
stream->print(action.c_str());
|
||||||
stream->print("</td>");
|
stream->print("</td>");
|
||||||
|
stream->print("</tr>");
|
||||||
}
|
}
|
||||||
|
|
||||||
UrlMatch match_url(const std::string &url, bool only_domain = false) {
|
UrlMatch match_url(const std::string &url, bool only_domain = false) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
voluptuous==0.11.7
|
voluptuous==0.11.7
|
||||||
PyYAML==5.3.1
|
PyYAML==5.3.1
|
||||||
paho-mqtt==1.5.0
|
paho-mqtt==1.5.1
|
||||||
colorlog==4.2.1
|
colorlog==4.2.1
|
||||||
tornado==6.0.4
|
tornado==6.0.4
|
||||||
protobuf==3.13.0
|
protobuf==3.13.0
|
||||||
|
|
|
@ -6,7 +6,7 @@ pexpect==4.8.0
|
||||||
|
|
||||||
# Unit tests
|
# Unit tests
|
||||||
pytest==6.0.2
|
pytest==6.0.2
|
||||||
pytest-cov==2.10.0
|
pytest-cov==2.10.1
|
||||||
pytest-mock==3.3.1
|
pytest-mock==3.3.1
|
||||||
asyncmock==0.4.2
|
asyncmock==0.4.2
|
||||||
hypothesis==5.21.0
|
hypothesis==5.21.0
|
||||||
|
|
|
@ -8,6 +8,7 @@ esphome:
|
||||||
- wait_until:
|
- wait_until:
|
||||||
- api.connected
|
- api.connected
|
||||||
- wifi.connected
|
- wifi.connected
|
||||||
|
- time.has_time
|
||||||
includes:
|
includes:
|
||||||
- custom.h
|
- custom.h
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue