Merge pull request #28 from sijk/external_component

Make this usable as an `external_component`
This commit is contained in:
Geoff Davis 2021-05-27 10:24:07 -07:00 committed by GitHub
commit c53dbc6e4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 82 deletions

3
.gitignore vendored
View File

@ -30,3 +30,6 @@
*.exe
*.out
*.app
# Python cache
__pycache__

111
README.md
View File

@ -11,7 +11,7 @@ ESP32 using the [ESPHome](https://esphome.io) framework.
## Requirements
* https://github.com/SwiCago/HeatPump
* ESPHome 1.15.0 or greater
* ESPHome 1.18.0 or greater
## Supported Microcontrollers
This library should work on most ESP8266 or ESP32 platforms. It has been tested
@ -50,53 +50,47 @@ to the control
board](https://github.com/SwiCago/HeatPump/issues/13#issuecomment-457897457)
via CN105.
### Step 2: Use ESPHome 1.15.0 or higher
### Step 2: Use ESPHome 1.18.0 or higher
The code in this repository makes use of a number of features in the 1.15.0 version of ESPHome, including various Fan modes.
The code in this repository makes use of a number of features in the 1.18.0
version of ESPHome, including various Fan modes and external components.
### Step 3: Clone this repository into your ESPHome configuration directory
### Step 3: Add this repository as an external component
This repository needs to live in your ESPHome configuration directory, as it
doesn't work correctly when used as a Platform.IO library, and there doesn't
seem to be an analog for that functionality for ESPHome code.
Add this repository to your ESPHome config:
On Hass.IO, you'll want to do something like:
```yaml
external_components:
- source: github://geoffdavis/esphome-mitsubishiheatpump
```
* Change directories to your esphome configuration directory.
* `mkdir -p src`
* `cd src`
* `git clone https://github.com/geoffdavis/esphome-mitsubishiheatpump.git`
### Step 4: Configure the heatpump
### Step 4: Configure your ESPHome device with YAML
Create an ESPHome YAML configuration with the following sections:
* `esphome: libraries: [https://github.com/SwiCago/HeatPump]`
* `esphome: includes: [src/esphome-mitsubishiheatpump]`
* `climate:` - set up a custom climate entry, change the Serial port as needed.
* ESP8266 only: `logger: baud_rate: 0` - disable serial port logging on the
sole ESP8266 hardware UART
The custom climate definition should use `platform: custom` and contain a
`lambda` block, where you instanciate an instance of the MitsubishiHeatPump
class, and then register it with ESPHome. It should allso contain a "climates"
entry. On ESP32 you
can change `&Serial` to `&Serial1` or `&Serial2` and re-enable logging to the
main serial port.
If that's all greek to you, here's an example. Change "My Heat Pump" to
whatever you want.
Add a `mitsubishi_heatpump` to your ESPHome config:
```yaml
climate:
- platform: custom
lambda: |-
auto my_heatpump = new MitsubishiHeatPump(&Serial);
App.register_component(my_heatpump);
return {my_heatpump};
climates:
- name: "My Heat Pump"
- platform: mitsubishi_heatpump
name: "My Heat Pump"
# Optional
hardware_uart: UART0
# Optional
update_period: 500ms
```
On ESP8266 you'll need to disable logging to serial because it conflicts with
the heatpump UART:
```yaml
logger:
baud_rate: 0
```
On ESP32 you can change `hardware_uart` to `UART1` or `UART2` and keep logging
enabled on the main serial port.
Note: this component DOES NOT use the ESPHome `uart` component, as it requires
direct access to a hardware UART via the Arduino `HardwareSerial` class. The
Mitsubishi Heatpump units use an atypical serial port setting ("even parity").
@ -119,12 +113,6 @@ esphome:
board: esp01_1m
# Boards tested: ESP-01S (ESP8266), Wemos D1 Mini (ESP8266); ESP32 Wifi-DevKit2
libraries:
- https://github.com/SwiCago/HeatPump
includes:
- src/esphome-mitsubishiheatpump
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
@ -181,18 +169,39 @@ sensor:
name: denheatpump WiFi Signal
update_interval: 60s
external_components:
- source: github://geoffdavis/esphome-mitsubishiheatpump
climate:
- platform: custom
# ESP32 only - change &Serial to &Serial1 or &Serial2 and remove the
- platform: mitsubishi_heatpump
name: "Den Heat Pump"
# ESP32 only - change UART0 to UART1 or UART2 and remove the
# logging:baud_rate above to allow the built-in UART0 to function for
# logging.
lambda: |-
auto my_heatpump = new MitsubishiHeatPump(&Serial);
App.register_component(my_heatpump);
return {my_heatpump};
climates:
- name: "Den Heat Pump"
hardware_uart: UART0
```
# Advanced
Some models of heat pump require different baud rates or don't support all
possible modes of operation. You can configure pretty much everything in YAML
to match what your hardware supports. For example:
```yaml
climate:
- platform: mitsubishi_heatpump
name: "My heat pump"
hardware_uart: UART2
baud_rate: 9600
supports:
mode: [AUTO, COOL, HEAT, FAN_ONLY]
fan_mode: [AUTO, LOW, MEDIUM, HIGH]
swing_mode: [OFF, VERTICAL]
visual:
min_temperature: 16
max_temperature: 31
temperature_step: 1.0
```
# See Also

View File

@ -0,0 +1,88 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate
from esphome.components.logger import HARDWARE_UART_TO_SERIAL
from esphome.const import (
CONF_ID,
CONF_HARDWARE_UART,
CONF_BAUD_RATE,
CONF_UPDATE_INTERVAL,
CONF_MODE,
CONF_FAN_MODE,
CONF_SWING_MODE,
)
from esphome.core import CORE, coroutine
AUTO_LOAD = ["climate"]
CONF_SUPPORTS = "supports"
DEFAULT_CLIMATE_MODES = ['AUTO', 'COOL', 'HEAT', 'DRY', 'FAN_ONLY']
DEFAULT_FAN_MODES = ['AUTO', 'DIFFUSE', 'LOW', 'MEDIUM', 'MIDDLE', 'HIGH']
DEFAULT_SWING_MODES = ['OFF', 'VERTICAL']
MitsubishiHeatPump = cg.global_ns.class_("MitsubishiHeatPump", climate.Climate, cg.PollingComponent)
def valid_uart(uart):
if CORE.is_esp8266:
uarts = ["UART0"] # UART1 is tx-only
elif CORE.is_esp32:
uarts = ["UART0", "UART1", "UART2"]
else:
raise NotImplementedError
return cv.one_of(*uarts, upper=True)(uart)
CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MitsubishiHeatPump),
cv.Optional(CONF_HARDWARE_UART, default="UART0"): valid_uart,
cv.Optional(CONF_BAUD_RATE): cv.positive_int,
# If polling interval is greater than 9 seconds, the HeatPump library
# reconnects, but doesn't then follow up with our data request.
cv.Optional(CONF_UPDATE_INTERVAL, default="500ms"): cv.All(
cv.update_interval,
cv.Range(max=cv.TimePeriod(milliseconds=9000))
),
# Optionally override the supported ClimateTraits.
cv.Optional(CONF_SUPPORTS, default={}): cv.Schema(
{
cv.Optional(CONF_MODE, default=DEFAULT_CLIMATE_MODES):
cv.ensure_list(climate.validate_climate_mode),
cv.Optional(CONF_FAN_MODE, default=DEFAULT_FAN_MODES):
cv.ensure_list(climate.validate_climate_fan_mode),
cv.Optional(CONF_SWING_MODE, default=DEFAULT_SWING_MODES):
cv.ensure_list(climate.validate_climate_swing_mode),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine
def to_code(config):
serial = HARDWARE_UART_TO_SERIAL[config[CONF_HARDWARE_UART]]
var = cg.new_Pvariable(config[CONF_ID], cg.RawExpression(f'&{serial}'))
if CONF_BAUD_RATE in config:
cg.add(var.set_baud_rate(config[CONF_BAUD_RATE]))
traits = []
for mode in config[CONF_SUPPORTS][CONF_MODE]:
if mode == 'OFF':
continue
traits.append(f'set_supports_{mode.lower()}_mode')
for mode in config[CONF_SUPPORTS][CONF_FAN_MODE]:
traits.append(f'set_supports_fan_mode_{mode.lower()}')
for mode in config[CONF_SUPPORTS][CONF_SWING_MODE]:
traits.append(f'set_supports_swing_mode_{mode.lower()}')
for trait in traits:
cg.add(getattr(var.config_traits(), trait)(True))
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
cg.add_library("https://github.com/SwiCago/HeatPump", None)

View File

@ -32,7 +32,15 @@ MitsubishiHeatPump::MitsubishiHeatPump(
) :
PollingComponent{poll_interval}, // member initializers list
hw_serial_{hw_serial}
{ }
{
this->traits_.set_supports_action(true);
this->traits_.set_supports_current_temperature(true);
this->traits_.set_supports_two_point_target_temperature(false);
this->traits_.set_supports_away(false);
this->traits_.set_visual_min_temperature(ESPMHP_MIN_TEMPERATURE);
this->traits_.set_visual_max_temperature(ESPMHP_MAX_TEMPERATURE);
this->traits_.set_visual_temperature_step(ESPMHP_TEMPERATURE_STEP);
}
void MitsubishiHeatPump::check_logger_conflict_() {
#ifdef USE_LOGGER
@ -56,8 +64,12 @@ void MitsubishiHeatPump::update() {
#endif
}
void MitsubishiHeatPump::set_baud_rate(int baud) {
this->baud_ = baud;
}
/**
* Define our supported traits.
* Get our supported traits.
*
* Note:
* Many of the following traits are only available in the 1.5.0 dev train of
@ -67,33 +79,17 @@ void MitsubishiHeatPump::update() {
* This class' supported climate::ClimateTraits.
*/
climate::ClimateTraits MitsubishiHeatPump::traits() {
auto traits = climate::ClimateTraits();
traits.set_supports_action(true);
traits.set_supports_current_temperature(true);
traits.set_supports_auto_mode(true);
traits.set_supports_cool_mode(true);
traits.set_supports_heat_mode(true);
traits.set_supports_dry_mode(true);
traits.set_supports_fan_only_mode(true);
traits.set_supports_two_point_target_temperature(false);
traits.set_supports_away(false);
traits.set_visual_min_temperature(ESPMHP_MIN_TEMPERATURE);
traits.set_visual_max_temperature(ESPMHP_MAX_TEMPERATURE);
traits.set_visual_temperature_step(ESPMHP_TEMPERATURE_STEP);
traits.set_supports_fan_mode_on(false);
traits.set_supports_fan_mode_off(false);
traits.set_supports_fan_mode_auto(true);
traits.set_supports_fan_mode_focus(false);
traits.set_supports_fan_mode_diffuse(true);
traits.set_supports_fan_mode_low(true);
traits.set_supports_fan_mode_medium(true);
traits.set_supports_fan_mode_middle(true);
traits.set_supports_fan_mode_high(true);
traits.set_supports_swing_mode_off(true);
traits.set_supports_swing_mode_both(false);
traits.set_supports_swing_mode_vertical(true);
traits.set_supports_swing_mode_horizontal(false);
return traits;
return traits_;
}
/**
* Modify our supported traits.
*
* Returns:
* A reference to this class' supported climate::ClimateTraits.
*/
climate::ClimateTraits& MitsubishiHeatPump::config_traits() {
return traits_;
}
/**
@ -451,7 +447,7 @@ void MitsubishiHeatPump::setup() {
ESP_LOGCONFIG(TAG, "Calling hp->connect(%p)", this->get_hw_serial_());
if (hp->connect(this->get_hw_serial_())) {
if (hp->connect(this->get_hw_serial_(), this->baud_)) {
hp->sync();
}
else {

View File

@ -62,6 +62,9 @@ class MitsubishiHeatPump : public PollingComponent, public climate::Climate {
ESPMHP_VERSION);
}
// Set the baud rate. Must be called before setup() to have any effect.
void set_baud_rate(int);
// print the current configuration
void dump_config() override;
@ -80,6 +83,9 @@ class MitsubishiHeatPump : public PollingComponent, public climate::Climate {
// Configure the climate object with traits that we support.
climate::ClimateTraits traits() override;
// Get a mutable reference to the traits that we support.
climate::ClimateTraits& config_traits();
// Debugging function to print the object's state.
void dump_state();
@ -90,6 +96,9 @@ class MitsubishiHeatPump : public PollingComponent, public climate::Climate {
// HeatPump object using the underlying Arduino library.
HeatPump* hp;
// The ClimateTraits supported by this HeatPump.
climate::ClimateTraits traits_;
// Allow the HeatPump class to use get_hw_serial_
friend class HeatPump;
@ -118,7 +127,7 @@ class MitsubishiHeatPump : public PollingComponent, public climate::Climate {
private:
// Retrieve the HardwareSerial pointer from friend and subclasses.
HardwareSerial *hw_serial_;
int baud_ = 0;
};
#endif