mirror of
https://github.com/esphome/esphome.git
synced 2024-12-23 22:14:54 +01:00
Fujitsu General climate new component (#677)
* new Fujitsu-General climate component * Refactor out climate_ir CC @glmnet Refactored out climate_ir python files too. Fixed invalid namespace name for climate_ir. * Add namespace lint check * Refactor Fujitsu Climate to climate_ir Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
929600d7f7
commit
6ae59bb43d
12 changed files with 332 additions and 61 deletions
|
@ -0,0 +1,41 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate, remote_transmitter, remote_receiver, sensor, remote_base
|
||||||
|
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
|
||||||
|
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
|
||||||
|
from esphome.core import coroutine
|
||||||
|
|
||||||
|
AUTO_LOAD = ['sensor', 'remote_base']
|
||||||
|
|
||||||
|
climate_ir_ns = cg.esphome_ns.namespace('climate_ir')
|
||||||
|
ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component,
|
||||||
|
remote_base.RemoteReceiverListener)
|
||||||
|
|
||||||
|
CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend({
|
||||||
|
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
|
||||||
|
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend({
|
||||||
|
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine
|
||||||
|
def register_climate_ir(var, config):
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield climate.register_climate(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
||||||
|
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
||||||
|
if CONF_SENSOR in config:
|
||||||
|
sens = yield cg.get_variable(config[CONF_SENSOR])
|
||||||
|
cg.add(var.set_sensor(sens))
|
||||||
|
if CONF_RECEIVER_ID in config:
|
||||||
|
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
||||||
|
cg.add(receiver.register_listener(var))
|
||||||
|
|
||||||
|
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||||
|
cg.add(var.set_transmitter(transmitter))
|
|
@ -2,7 +2,7 @@
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace climate {
|
namespace climate_ir {
|
||||||
|
|
||||||
static const char *TAG = "climate_ir";
|
static const char *TAG = "climate_ir";
|
||||||
|
|
||||||
|
@ -63,5 +63,5 @@ void ClimateIR::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
|
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace climate
|
} // namespace climate_ir
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace climate {
|
namespace climate_ir {
|
||||||
|
|
||||||
/* A base for climate which works by sending (and receiving) IR codes
|
/* A base for climate which works by sending (and receiving) IR codes
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
|
||||||
climate::ClimateTraits traits() override;
|
climate::ClimateTraits traits() override;
|
||||||
|
|
||||||
/// Transmit via IR the state of this climate controller.
|
/// Transmit via IR the state of this climate controller.
|
||||||
virtual void transmit_state() {}
|
virtual void transmit_state() = 0;
|
||||||
|
|
||||||
bool supports_cool_{true};
|
bool supports_cool_{true};
|
||||||
bool supports_heat_{true};
|
bool supports_heat_{true};
|
||||||
|
@ -50,5 +50,6 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
|
||||||
remote_transmitter::RemoteTransmitterComponent *transmitter_;
|
remote_transmitter::RemoteTransmitterComponent *transmitter_;
|
||||||
sensor::Sensor *sensor_{nullptr};
|
sensor::Sensor *sensor_{nullptr};
|
||||||
};
|
};
|
||||||
} // namespace climate
|
|
||||||
|
} // namespace climate_ir
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,37 +1,18 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import climate, remote_transmitter, remote_receiver, sensor
|
from esphome.components import climate_ir
|
||||||
from esphome.components.remote_base import CONF_TRANSMITTER_ID, CONF_RECEIVER_ID
|
from esphome.const import CONF_ID
|
||||||
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
|
||||||
|
|
||||||
AUTO_LOAD = ['sensor', 'climate_ir']
|
AUTO_LOAD = ['climate_ir']
|
||||||
|
|
||||||
coolix_ns = cg.esphome_ns.namespace('coolix')
|
coolix_ns = cg.esphome_ns.namespace('coolix')
|
||||||
CoolixClimate = coolix_ns.class_('CoolixClimate', climate.Climate, cg.Component)
|
CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(CoolixClimate),
|
cv.GenerateID(): cv.declare_id(CoolixClimate),
|
||||||
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
|
})
|
||||||
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
|
|
||||||
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
|
|
||||||
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
|
||||||
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
|
||||||
}).extend(cv.COMPONENT_SCHEMA))
|
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
yield climate_ir.register_climate_ir(var, config)
|
||||||
yield climate.register_climate(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
|
||||||
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
|
||||||
if CONF_SENSOR in config:
|
|
||||||
sens = yield cg.get_variable(config[CONF_SENSOR])
|
|
||||||
cg.add(var.set_sensor(sens))
|
|
||||||
if CONF_RECEIVER_ID in config:
|
|
||||||
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
|
||||||
cg.add(receiver.register_listener(var))
|
|
||||||
|
|
||||||
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
|
|
||||||
cg.add(var.set_transmitter(transmitter))
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ namespace coolix {
|
||||||
const uint8_t COOLIX_TEMP_MIN = 17; // Celsius
|
const uint8_t COOLIX_TEMP_MIN = 17; // Celsius
|
||||||
const uint8_t COOLIX_TEMP_MAX = 30; // Celsius
|
const uint8_t COOLIX_TEMP_MAX = 30; // Celsius
|
||||||
|
|
||||||
class CoolixClimate : public climate::ClimateIR {
|
class CoolixClimate : public climate_ir::ClimateIR {
|
||||||
public:
|
public:
|
||||||
CoolixClimate() : climate::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX) {}
|
CoolixClimate() : climate_ir::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Transmit via IR the state of this climate controller.
|
/// Transmit via IR the state of this climate controller.
|
||||||
|
|
0
esphome/components/fujitsu_general/__init__.py
Normal file
0
esphome/components/fujitsu_general/__init__.py
Normal file
18
esphome/components/fujitsu_general/climate.py
Normal file
18
esphome/components/fujitsu_general/climate.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate_ir
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
AUTO_LOAD = ['climate_ir']
|
||||||
|
|
||||||
|
fujitsu_general_ns = cg.esphome_ns.namespace('fujitsu_general')
|
||||||
|
FujitsuGeneralClimate = fujitsu_general_ns.class_('FujitsuGeneralClimate', climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield climate_ir.register_climate_ir(var, config)
|
212
esphome/components/fujitsu_general/fujitsu_general.cpp
Normal file
212
esphome/components/fujitsu_general/fujitsu_general.cpp
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
#include "fujitsu_general.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace fujitsu_general {
|
||||||
|
|
||||||
|
static const char *TAG = "fujitsu_general.climate";
|
||||||
|
|
||||||
|
// Control packet
|
||||||
|
const uint16_t FUJITSU_GENERAL_STATE_LENGTH = 16;
|
||||||
|
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE0 = 0x14;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE1 = 0x63;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE2 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE3 = 0x10;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE4 = 0x10;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE5 = 0xFE;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE6 = 0x09;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE7 = 0x30;
|
||||||
|
|
||||||
|
// Temperature and POWER ON
|
||||||
|
const uint8_t FUJITSU_GENERAL_POWER_ON_MASK_BYTE8 = 0b00000001;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE8 = 0x40;
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_AUTO_BYTE9 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_HEAT_BYTE9 = 0x04;
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_COOL_BYTE9 = 0x01;
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_DRY_BYTE9 = 0x02;
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_FAN_BYTE9 = 0x03;
|
||||||
|
const uint8_t FUJITSU_GENERAL_MODE_10C_BYTE9 = 0x0B;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE9 = 0x01;
|
||||||
|
|
||||||
|
// Fan speed and swing
|
||||||
|
const uint8_t FUJITSU_GENERAL_FAN_AUTO_BYTE10 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01;
|
||||||
|
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
|
||||||
|
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
|
||||||
|
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
|
||||||
|
const uint8_t FUJITSU_GENERAL_SWING_MASK_BYTE10 = 0b00010000;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
|
||||||
|
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE12 = 0x00;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE13 = 0x00;
|
||||||
|
|
||||||
|
// Outdoor Unit Low Noise
|
||||||
|
const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE14 = 0x20;
|
||||||
|
|
||||||
|
// CRC
|
||||||
|
const uint8_t FUJITSU_GENERAL_BASE_BYTE15 = 0x6F;
|
||||||
|
|
||||||
|
// Power off packet is specific
|
||||||
|
const uint16_t FUJITSU_GENERAL_OFF_LENGTH = 7;
|
||||||
|
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE0 = FUJITSU_GENERAL_BASE_BYTE0;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE1 = FUJITSU_GENERAL_BASE_BYTE1;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE2 = FUJITSU_GENERAL_BASE_BYTE2;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE3 = FUJITSU_GENERAL_BASE_BYTE3;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE4 = FUJITSU_GENERAL_BASE_BYTE4;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE5 = 0x02;
|
||||||
|
const uint8_t FUJITSU_GENERAL_OFF_BYTE6 = 0xFD;
|
||||||
|
|
||||||
|
const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
|
||||||
|
const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius
|
||||||
|
|
||||||
|
const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
|
||||||
|
const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
|
||||||
|
const uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
|
||||||
|
const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
|
||||||
|
const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
|
||||||
|
const uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
|
||||||
|
const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
|
||||||
|
|
||||||
|
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
|
||||||
|
|
||||||
|
FujitsuGeneralClimate::FujitsuGeneralClimate() : ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1) {}
|
||||||
|
|
||||||
|
void FujitsuGeneralClimate::transmit_state() {
|
||||||
|
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||||
|
this->transmit_off_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t remote_state[FUJITSU_GENERAL_STATE_LENGTH] = {0};
|
||||||
|
|
||||||
|
remote_state[0] = FUJITSU_GENERAL_BASE_BYTE0;
|
||||||
|
remote_state[1] = FUJITSU_GENERAL_BASE_BYTE1;
|
||||||
|
remote_state[2] = FUJITSU_GENERAL_BASE_BYTE2;
|
||||||
|
remote_state[3] = FUJITSU_GENERAL_BASE_BYTE3;
|
||||||
|
remote_state[4] = FUJITSU_GENERAL_BASE_BYTE4;
|
||||||
|
remote_state[5] = FUJITSU_GENERAL_BASE_BYTE5;
|
||||||
|
remote_state[6] = FUJITSU_GENERAL_BASE_BYTE6;
|
||||||
|
remote_state[7] = FUJITSU_GENERAL_BASE_BYTE7;
|
||||||
|
remote_state[8] = FUJITSU_GENERAL_BASE_BYTE8;
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_BASE_BYTE9;
|
||||||
|
remote_state[10] = FUJITSU_GENERAL_BASE_BYTE10;
|
||||||
|
remote_state[11] = FUJITSU_GENERAL_BASE_BYTE11;
|
||||||
|
remote_state[12] = FUJITSU_GENERAL_BASE_BYTE12;
|
||||||
|
remote_state[13] = FUJITSU_GENERAL_BASE_BYTE13;
|
||||||
|
remote_state[14] = FUJITSU_GENERAL_BASE_BYTE14;
|
||||||
|
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
|
||||||
|
|
||||||
|
// Set temperature
|
||||||
|
uint8_t safecelsius = std::max((uint8_t) this->target_temperature, FUJITSU_GENERAL_TEMP_MIN);
|
||||||
|
safecelsius = std::min(safecelsius, FUJITSU_GENERAL_TEMP_MAX);
|
||||||
|
remote_state[8] = (byte) safecelsius - 16;
|
||||||
|
remote_state[8] = remote_state[8] << 4;
|
||||||
|
|
||||||
|
// If not powered - set power on flag
|
||||||
|
if (!this->power_) {
|
||||||
|
remote_state[8] = (byte) remote_state[8] | FUJITSU_GENERAL_POWER_ON_MASK_BYTE8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set mode
|
||||||
|
switch (this->mode) {
|
||||||
|
case climate::CLIMATE_MODE_COOL:
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_MODE_COOL_BYTE9;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_AUTO:
|
||||||
|
default:
|
||||||
|
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
|
||||||
|
break;
|
||||||
|
// TODO: CLIMATE_MODE_FAN_ONLY, CLIMATE_MODE_DRY, CLIMATE_MODE_10C are missing in esphome
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: missing support for fan speed
|
||||||
|
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
|
||||||
|
|
||||||
|
// TODO: missing support for swing
|
||||||
|
// remote_state[10] = (byte) remote_state[10] | FUJITSU_GENERAL_SWING_MASK_BYTE10;
|
||||||
|
|
||||||
|
// TODO: missing support for outdoor unit low noise
|
||||||
|
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
|
||||||
|
|
||||||
|
// CRC
|
||||||
|
remote_state[15] = 0;
|
||||||
|
for (int i = 7; i < 15; i++) {
|
||||||
|
remote_state[15] += (byte) remote_state[i]; // Addiction
|
||||||
|
}
|
||||||
|
remote_state[15] = 0x100 - remote_state[15]; // mod 256
|
||||||
|
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto data = transmit.get_data();
|
||||||
|
|
||||||
|
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
data->mark(FUJITSU_GENERAL_HEADER_MARK);
|
||||||
|
data->space(FUJITSU_GENERAL_HEADER_SPACE);
|
||||||
|
// Data
|
||||||
|
for (uint8_t i : remote_state) {
|
||||||
|
// Send all Bits from Byte Data in Reverse Order
|
||||||
|
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||||
|
data->mark(FUJITSU_GENERAL_BIT_MARK);
|
||||||
|
bool bit = i & mask;
|
||||||
|
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
|
||||||
|
// Next bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Footer
|
||||||
|
data->mark(FUJITSU_GENERAL_TRL_MARK);
|
||||||
|
data->space(FUJITSU_GENERAL_TRL_SPACE);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
|
||||||
|
this->power_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FujitsuGeneralClimate::transmit_off_() {
|
||||||
|
uint8_t remote_state[FUJITSU_GENERAL_OFF_LENGTH] = {0};
|
||||||
|
|
||||||
|
remote_state[0] = FUJITSU_GENERAL_OFF_BYTE0;
|
||||||
|
remote_state[1] = FUJITSU_GENERAL_OFF_BYTE1;
|
||||||
|
remote_state[2] = FUJITSU_GENERAL_OFF_BYTE2;
|
||||||
|
remote_state[3] = FUJITSU_GENERAL_OFF_BYTE3;
|
||||||
|
remote_state[4] = FUJITSU_GENERAL_OFF_BYTE4;
|
||||||
|
remote_state[5] = FUJITSU_GENERAL_OFF_BYTE5;
|
||||||
|
remote_state[6] = FUJITSU_GENERAL_OFF_BYTE6;
|
||||||
|
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto data = transmit.get_data();
|
||||||
|
|
||||||
|
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
data->mark(FUJITSU_GENERAL_HEADER_MARK);
|
||||||
|
data->space(FUJITSU_GENERAL_HEADER_SPACE);
|
||||||
|
|
||||||
|
// Data
|
||||||
|
for (uint8_t i : remote_state) {
|
||||||
|
// Send all Bits from Byte Data in Reverse Order
|
||||||
|
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||||
|
data->mark(FUJITSU_GENERAL_BIT_MARK);
|
||||||
|
bool bit = i & mask;
|
||||||
|
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
|
||||||
|
// Next bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Footer
|
||||||
|
data->mark(FUJITSU_GENERAL_TRL_MARK);
|
||||||
|
data->space(FUJITSU_GENERAL_TRL_SPACE);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
|
||||||
|
this->power_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fujitsu_general
|
||||||
|
} // namespace esphome
|
24
esphome/components/fujitsu_general/fujitsu_general.h
Normal file
24
esphome/components/fujitsu_general/fujitsu_general.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/climate_ir/climate_ir.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace fujitsu_general {
|
||||||
|
|
||||||
|
class FujitsuGeneralClimate : public climate_ir::ClimateIR {
|
||||||
|
public:
|
||||||
|
FujitsuGeneralClimate();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Transmit via IR the state of this climate controller.
|
||||||
|
void transmit_state() override;
|
||||||
|
/// Transmit via IR power off command.
|
||||||
|
void transmit_off_();
|
||||||
|
|
||||||
|
bool power_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fujitsu_general
|
||||||
|
} // namespace esphome
|
|
@ -1,37 +1,18 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import climate, remote_transmitter, remote_receiver, sensor
|
from esphome.components import climate_ir
|
||||||
from esphome.components.remote_base import CONF_TRANSMITTER_ID, CONF_RECEIVER_ID
|
from esphome.const import CONF_ID
|
||||||
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
|
||||||
|
|
||||||
AUTO_LOAD = ['sensor', 'climate_ir']
|
AUTO_LOAD = ['climate_ir']
|
||||||
|
|
||||||
tcl112_ns = cg.esphome_ns.namespace('tcl112')
|
tcl112_ns = cg.esphome_ns.namespace('tcl112')
|
||||||
Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate.Climate, cg.Component)
|
Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(Tcl112Climate),
|
cv.GenerateID(): cv.declare_id(Tcl112Climate),
|
||||||
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
|
})
|
||||||
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
|
|
||||||
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
|
|
||||||
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
|
||||||
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
|
||||||
}).extend(cv.COMPONENT_SCHEMA))
|
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
yield climate_ir.register_climate_ir(var, config)
|
||||||
yield climate.register_climate(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
|
||||||
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
|
||||||
if CONF_SENSOR in config:
|
|
||||||
sens = yield cg.get_variable(config[CONF_SENSOR])
|
|
||||||
cg.add(var.set_sensor(sens))
|
|
||||||
if CONF_RECEIVER_ID in config:
|
|
||||||
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
|
||||||
cg.add(receiver.register_listener(var))
|
|
||||||
|
|
||||||
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
|
|
||||||
cg.add(var.set_transmitter(transmitter))
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ namespace tcl112 {
|
||||||
const float TCL112_TEMP_MAX = 31.0;
|
const float TCL112_TEMP_MAX = 31.0;
|
||||||
const float TCL112_TEMP_MIN = 16.0;
|
const float TCL112_TEMP_MIN = 16.0;
|
||||||
|
|
||||||
class Tcl112Climate : public climate::ClimateIR {
|
class Tcl112Climate : public climate_ir::ClimateIR {
|
||||||
public:
|
public:
|
||||||
Tcl112Climate() : climate::ClimateIR(TCL112_TEMP_MIN, TCL112_TEMP_MAX, .5f) {}
|
Tcl112Climate() : climate_ir::ClimateIR(TCL112_TEMP_MIN, TCL112_TEMP_MAX, .5f) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Transmit via IR the state of this climate controller.
|
/// Transmit via IR the state of this climate controller.
|
||||||
|
|
|
@ -298,6 +298,19 @@ def lint_relative_py_import(fname):
|
||||||
' from . import abc_ns\n\n')
|
' from . import abc_ns\n\n')
|
||||||
|
|
||||||
|
|
||||||
|
@lint_content_check(include=['esphome/components/*.h', 'esphome/components/*.cpp',
|
||||||
|
'esphome/components/*.tcc'])
|
||||||
|
def lint_namespace(fname, content):
|
||||||
|
expected_name = re.match(r'^esphome/components/([^/]+)/.*',
|
||||||
|
fname.replace(os.path.sep, '/')).group(1)
|
||||||
|
search = 'namespace {}'.format(expected_name)
|
||||||
|
if search in content:
|
||||||
|
return None
|
||||||
|
return 'Invalid namespace found in C++ file. All integration C++ files should put all ' \
|
||||||
|
'functions in a separate namespace that matches the integration\'s name. ' \
|
||||||
|
'Please make sure the file contains {}'.format(highlight(search))
|
||||||
|
|
||||||
|
|
||||||
@lint_content_find_check('"esphome.h"', include=cpp_include, exclude=['tests/custom.h'])
|
@lint_content_find_check('"esphome.h"', include=cpp_include, exclude=['tests/custom.h'])
|
||||||
def lint_esphome_h(fname):
|
def lint_esphome_h(fname):
|
||||||
return ("File contains reference to 'esphome.h' - This file is "
|
return ("File contains reference to 'esphome.h' - This file is "
|
||||||
|
|
Loading…
Reference in a new issue