Merge branch 'release/2.0.0'

Convert to ESPHome custom component.
This commit is contained in:
Geoff Davis 2021-05-27 13:39:44 -07:00
commit 128056efb2
6 changed files with 275 additions and 99 deletions

3
.gitignore vendored
View File

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

204
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,60 +50,85 @@ 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](https://esphome.io/components/external_components.html).
### 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 3a: Upgrading from 1.x releases
### Step 4: Configure your ESPHome device with YAML
Version 2.0 and greater of this libary use the ESPHome `external_components`
feature, which is a huge step forward in terms of usability. In order to make
things compile correctly, you will need to:
1. Remove the `libraries` section that imports
`https://github.com/SwiCago/HeatPump`, as this is handled by the
`external_component` section of manifest.
2. Remove the `includes` section that imports `src/esphome-mitsubishiheatpump`
3. Delete the old checkout of this repository under
`src/esphome-mitsubishiheatpump`.
4. Clean your old ESPHome build directories out (3-dot menu, "Clean Build
Files")
5. You may also have to delete the _esphomenodename_ directory that
corresponds with your _esphomenodename.yaml_ configuration file
completely. Testing with ESPHome 0.18.x showed this to be necessary to get
the cached copy of src/esphome-mitsubishiheatpump to go away entirely, as
the "Clean Build Files" isn't as thorough as one would like.
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
*Note:* Failure to delete the old source directory and remove the `includes`
and `libraries` lines will likely result in compilation errors complaining
about duplicate declarations of `MitsubishiHeatPump::traits()`.
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.
##### Example error
```
Linking /data/bedroom_east_heatpump/.pioenvs/bedroom_east_heatpump/firmware.elf
/root/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/4.8.2/../../../../xtensa-lx106-elf/bin/ld: /data/bedroom_east_heatpump/.pioenvs/bedroom_east_heatpump/src/esphome/components/mitsubishi_heatpump/espmhp.cpp.o: in function `MitsubishiHeatPump::traits()':
espmhp.cpp:(.text._ZN18MitsubishiHeatPump6traitsEv+0x4): multiple definition of `MitsubishiHeatPump::traits()'; /data/bedroom_east_heatpump/.pioenvs/bedroom_east_heatpump/src/esphome-mitsubishiheatpump/espmhp.cpp.o:espmhp.cpp:(.text._ZN18MitsubishiHeatPump6traitsEv+0x80): first defined here
```
If that's all greek to you, here's an example. Change "My Heat Pump" to
whatever you want.
### Step 4: Configure the heatpump
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_interval: 500ms
```
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").
Parity bit support is not implemented in any of the existing software serial
libraries, including the one in ESPHome. There's currently no way to guarantee
access to a hardware UART nor retrieve the `HardwareSerial` handle from the
`uart` component within the ESPHome framework.
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"). Parity bit support is not implemented in any of the existing
software serial libraries, including the one in ESPHome. There's currently no
way to guarantee access to a hardware UART nor retrieve the `HardwareSerial`
handle from the `uart` component within the ESPHome framework.
# Example configuration
@ -119,12 +144,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
@ -134,6 +153,14 @@ wifi:
ssid: "Denheatpump Fallback Hotspot"
password: !secret fallback_password
# Note: if upgrading from 1.x releases of esphome-mitsubishiheatpump, be sure
# to remove any old entries from the `libraries` and `includes` section.
libraries:
# Remove reference to SwiCago/HeatPump
includes:
# Remove reference to src/esphome-mitsubishiheatpump
captive_portal:
# Enable logging
@ -181,29 +208,79 @@ 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 configuration
Some models of heat pump require different baud rates or don't support all
possible modes of operation. You can configure mulitple climate "traits" 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
```
## Configuration variables that affect this library directly
* *hardware\_uart* (_Optional_): the hardware UART instance to use for
communcation with the heatpump. On ESP8266, only `UART0` is usable. On ESP32,
`UART0`, `UART1`, and `UART2` are all valid choices. Default: `UART0`
* *baud\_rate* (_Optional_): Serial BAUD rate used to communicate with the
HeatPump. Most systems use the default value of `4800` baud, but some use
`9600`. Default: `4800`
* *update\_interval* (_Optional_, range: 0ms to 9000ms): How often this
component polls the heatpump hardware, in milliseconds. Maximum usable value
is 9 seconds due to underlying issues with the HeatPump library. Default: 500ms
* *supports* (_Optional_): Supported features for the device. ** *mode*
(_Optional_, list): Supported climate modes for the HeatPump. Default:
`['AUTO', 'COOL', 'HEAT', 'DRY', 'FAN_ONLY']`
** *fan_mode* (_Optional_, list):
Supported fan speeds for the HeatPump. Default: `['AUTO', 'DIFFUSE', 'LOW',
'MEDIUM', 'MIDDLE', 'HIGH']` ** *swing_mode* (_Optional_, list): Supported
fan swing modes. Most Mitsubishi units only support the default. Default:
`['OFF', 'VERTICAL']`
## Other configuration
* *id* (_Optional_): used to identify multiple instances, e.g. "denheatpump"
* *name* (_Required_): The name of the climate component, e.g. "Den Heatpump"
* *visual* (_Optional_): The core `Climate` component has several *visual*
options that can be set. See the [Climate
Component](https://esphome.io/components/climate/index.html) documentation for
details.
# See Also
## Other Implementations
The [gysmo38/mitsubishi2MQTT](https://github.com/gysmo38/mitsubishi2MQTT)
Arduino sketch also uses the `SwiCago/HeatPump`
library, and works with MQTT directly. The author found it's WiFi stack to not
be particularly robust, but the controls worked fine. Like this ESPHome
repository, it will automatically register the device in your HomeAssistant
instance if you have HA configured to do so.
Arduino sketch also uses the `SwiCago/HeatPump`
library, and works with MQTT directly. The author of this implementation found
`mitsubishi2MQTT`'s WiFi stack to not be particularly robust, but the controls
worked fine. Like this ESPHome repository, `mitsubishi2MQTT` will automatically
register the device in your HomeAssistant instance if you have HA configured to do so.
There's also the built-in to ESPHome
[Mitsubishi](https://github.com/esphome/esphome/blob/dev/esphome/components/mitsubishi/mitsubishi.h)
@ -217,6 +294,7 @@ the settings via an IR remote.
## Reference documentation
The author referred to the following documentation repeatedly:
* https://esphome.io/components/sensor/custom.html
* https://esphome.io/components/climate/custom.html
* Source for ESPHome's Dev branch: https://github.com/esphome/esphome/tree/dev/esphome/components/climate
* [ESPHome Custom Sensors Reference](https://esphome.io/components/sensor/custom.html)
* [ESPHome Custom Climate Components Reference](https://esphome.io/components/climate/custom.html)
* [ESPHome External Components Reference](https://esphome.io/components/external_components.html)
* [Source for ESPHome's Climate Component](https://github.com/esphome/esphome/tree/master/esphome/components/climate)

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

@ -8,12 +8,13 @@
* Author: Barry Loong @loongyh on GitHub.
* Author: @am-io on Github.
* Author: @nao-pon on Github.
* Last Updated: 2020-07-06
* Author: Simon Knopp @sijk on Github
* Last Updated: 2021-05-27
* License: BSD
*
* Requirements:
* - https://github.com/SwiCago/HeatPump
* - ESPHome 1.15.0 or greater
* - ESPHome 1.18.0 or greater
*/
#include "espmhp.h"
@ -32,7 +33,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 +65,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 +80,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 +448,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

@ -6,12 +6,13 @@
* Author: Geoff Davis <geoff@geoffdavis.com>
* Author: Phil Genera @pgenera on Github.
* Author: @nao-pon on Github
* Last Updated: 2021-04-28
* Author: Simon Knopp @sijk on Github
* Last Updated: 2021-05-27
* License: BSD
*
* Requirements:
* - https://github.com/SwiCago/HeatPump
* - ESPHome 1.15.0 or greater
* - ESPHome 1.18.0 or greater
*/
#define USE_CALLBACKS
@ -27,7 +28,7 @@ using namespace esphome;
static const char* TAG = "MitsubishiHeatPump"; // Logging tag
static const char* ESPMHP_VERSION = "1.3.1";
static const char* ESPMHP_VERSION = "2.0.0";
/* If polling interval is greater than 9 seconds, the HeatPump
library reconnects, but doesn't then follow up with our data request.*/
@ -62,6 +63,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 +84,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 +97,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 +128,7 @@ class MitsubishiHeatPump : public PollingComponent, public climate::Climate {
private:
// Retrieve the HardwareSerial pointer from friend and subclasses.
HardwareSerial *hw_serial_;
int baud_ = 0;
};
#endif