Merge pull request #17 from ElVit/heatpump_unittests

🚀 Add Comprehensive Tests and CI for Panasonic Heatpump
This commit is contained in:
ElVit 2025-12-01 19:24:51 +01:00 committed by GitHub
commit c3c6106ade
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 7244 additions and 5412 deletions

66
.clang-format Normal file
View File

@ -0,0 +1,66 @@
---
# Clang-format configuration for ESPHome components
# Based on ESPHome coding style
BasedOnStyle: Google
IndentWidth: 2
ColumnLimit: 120
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++11
UseTab: Never

View File

@ -0,0 +1,184 @@
name: Test Panasonic Heatpump Component
on:
push:
branches:
- main
- 'heatpump**'
paths:
- 'components/panasonic_heatpump/**'
- 'tests/panasonic_heatpump/**'
- '.github/workflows/test_panasonic_heatpump.yml'
pull_request:
branches:
- main
paths:
- 'components/panasonic_heatpump/**'
- 'tests/panasonic_heatpump/**'
- '.github/workflows/test_panasonic_heatpump.yml'
workflow_dispatch:
jobs:
test-build:
name: Test ESPHome Build
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
board: [esp8266, esp32s2, esp32c3, full]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build ESPHome firmware for ${{ matrix.board }}
uses: esphome/build-action@v7
with:
yaml-file: tests/panasonic_heatpump/test_panasonic_heatpump_${{ matrix.board == 'full' && 'full' || matrix.board }}.yaml
version: latest
lint-code:
name: Lint C++ Code
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install clang-format
run: |
sudo apt-get update
sudo apt-get install -y clang-format
- name: Run clang-format check
run: |
find components/panasonic_heatpump -name '*.cpp' -o -name '*.h' | \
xargs clang-format -style=file --dry-run --Werror
lint-python:
name: Lint Python Code
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install linting tools
run: |
pip install --upgrade pip
pip install flake8 pylint black
- name: Run black check
run: |
black --check components/panasonic_heatpump/
- name: Run flake8
run: |
flake8 components/panasonic_heatpump/ --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 components/panasonic_heatpump/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
test-python:
name: Python Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install --upgrade pip
pip install pytest pytest-cov esphome
- name: Run Python unit tests
run: |
pytest tests/panasonic_heatpump/ -v --cov=components/panasonic_heatpump --cov-report=xml --cov-report=term
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-panasonic-heatpump
fail_ci_if_error: false
test-minimal-config:
name: Test Minimal Configuration
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ESPHome
run: |
pip install --upgrade pip
pip install esphome
- name: Validate minimal config
run: |
esphome config tests/panasonic_heatpump/test_panasonic_heatpump_mini.yaml
test-full-config:
name: Test Full Configuration
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ESPHome
run: |
pip install --upgrade pip
pip install esphome
- name: Validate full config
run: |
esphome config tests/panasonic_heatpump/test_panasonic_heatpump_full.yaml
summary:
name: Test Summary
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [test-build, lint-code, lint-python, test-python, test-minimal-config, test-full-config]
if: always()
steps:
- name: Check test results
run: |
echo "Test Build: ${{ needs.test-build.result }}"
echo "Lint C++: ${{ needs.lint-code.result }}"
echo "Lint Python: ${{ needs.lint-python.result }}"
echo "Python Unit Tests: ${{ needs.test-python.result }}"
echo "Minimal Config: ${{ needs.test-minimal-config.result }}"
echo "Full Config: ${{ needs.test-full-config.result }}"
if [ "${{ needs.test-build.result }}" != "success" ] || \
[ "${{ needs.lint-code.result }}" != "success" ] || \
[ "${{ needs.lint-python.result }}" != "success" ] || \
[ "${{ needs.test-python.result }}" != "success" ] || \
[ "${{ needs.test-minimal-config.result }}" != "success" ] || \
[ "${{ needs.test-full-config.result }}" != "success" ]; then
echo "One or more tests failed"
exit 1
fi
echo "All tests passed successfully!"

View File

@ -4,30 +4,32 @@ from esphome import pins
from esphome.components import uart
from esphome.const import CONF_ID
CODEOWNERS = ['@elvit']
CODEOWNERS = ["@elvit"]
MULTICONF = True
DEPENDENCIES = ['uart']
DEPENDENCIES = ["uart"]
CONF_MAIDESITE_DESK_ID = "maidesite_desk"
CONF_LOG_UART_MSG = "log_uart_msg"
maidesite_desk_ns = cg.esphome_ns.namespace('maidesite_desk')
MaidesiteDeskComponent = maidesite_desk_ns.class_('MaidesiteDeskComponent', cg.Component, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MaidesiteDeskComponent),
cv.Optional(CONF_LOG_UART_MSG, default=False): cv.boolean,
}
)
.extend(uart.UART_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
maidesite_desk_ns = cg.esphome_ns.namespace("maidesite_desk")
MaidesiteDeskComponent = maidesite_desk_ns.class_(
"MaidesiteDeskComponent", cg.Component, uart.UARTDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MaidesiteDeskComponent),
cv.Optional(CONF_LOG_UART_MSG, default=False): cv.boolean,
}
)
.extend(uart.UART_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
cg.add(var.set_log_uart_msg(config[CONF_LOG_UART_MSG]))
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
cg.add(var.set_log_uart_msg(config[CONF_LOG_UART_MSG]))

View File

@ -2,11 +2,10 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import button
from esphome.const import (
ENTITY_CATEGORY_CONFIG,
ENTITY_CATEGORY_CONFIG,
)
from .. import CONF_MAIDESITE_DESK_ID, MaidesiteDeskComponent, maidesite_desk_ns
CONF_STEP_UP = "step_up"
CONF_STEP_DOWN = "step_down"
CONF_STOP = "stop"
@ -22,79 +21,81 @@ CONF_SAVE_M3 = "save_m3"
CONF_SAVE_M4 = "save_m4"
TYPES = [
CONF_STEP_UP,
CONF_STEP_DOWN,
CONF_STOP,
CONF_GOTO_MAX,
CONF_GOTO_MIN,
CONF_GOTO_M1,
CONF_GOTO_M2,
CONF_GOTO_M3,
CONF_GOTO_M4,
CONF_SAVE_M1,
CONF_SAVE_M2,
CONF_SAVE_M3,
CONF_SAVE_M4,
CONF_STEP_UP,
CONF_STEP_DOWN,
CONF_STOP,
CONF_GOTO_MAX,
CONF_GOTO_MIN,
CONF_GOTO_M1,
CONF_GOTO_M2,
CONF_GOTO_M3,
CONF_GOTO_M4,
CONF_SAVE_M1,
CONF_SAVE_M2,
CONF_SAVE_M3,
CONF_SAVE_M4,
]
MaidesiteDeskButton = maidesite_desk_ns.class_("MaidesiteDeskButton", button.Button, cg.Component)
MaidesiteDeskButton = maidesite_desk_ns.class_(
"MaidesiteDeskButton", button.Button, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_STEP_UP): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_STEP_DOWN): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_STOP): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_MAX): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_MIN): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M1): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M2): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M3): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M4): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_SAVE_M1): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M2): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M3): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M4): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
}
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_STEP_UP): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_STEP_DOWN): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_STOP): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_MAX): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_MIN): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M1): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M2): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M3): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_GOTO_M4): button.button_schema(
MaidesiteDeskButton,
),
cv.Optional(CONF_SAVE_M1): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M2): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M3): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_SAVE_M4): button.button_schema(
MaidesiteDeskButton,
entity_category=ENTITY_CATEGORY_CONFIG,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await button.new_button(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_button")(var))
cg.add(var.set_id(index))
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await button.new_button(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_button")(var))
cg.add(var.set_id(index))

View File

@ -1,22 +1,17 @@
#include "maidesite_desk_button.h"
#include "esphome/core/log.h"
namespace esphome {
namespace maidesite_desk {
static const char* const TAG = "maidesite_desk.button";
namespace esphome
{
namespace maidesite_desk
{
static const char *const TAG = "maidesite_desk.button";
void MaidesiteDeskButton::dump_config() {
LOG_BUTTON("", "Maidesite Desk Button", this);
delay(10);
}
void MaidesiteDeskButton::dump_config()
{
LOG_BUTTON("", "Maidesite Desk Button", this);
delay(10);
}
void MaidesiteDeskButton::press_action()
{
this->parent_->button_press_action(this);
}
} // namespace maidesite_desk
} // namespace esphome
void MaidesiteDeskButton::press_action() {
this->parent_->button_press_action(this);
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -3,21 +3,19 @@
#include "esphome/core/component.h"
#include "../maidesite_desk.h"
namespace esphome {
namespace maidesite_desk {
class MaidesiteDeskButton : public button::Button, public Component, public Parented<MaidesiteDeskComponent> {
public:
MaidesiteDeskButton() = default;
void dump_config() override;
void set_id(int id) {
this->id_ = id;
}
namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskButton : public button::Button, public Component, public Parented<MaidesiteDeskComponent>
{
public:
MaidesiteDeskButton() = default;
void dump_config() override;
void set_id(int id) { this->id_ = id; }
protected:
void press_action() override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome
protected:
void press_action() override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -1,397 +1,359 @@
#include "maidesite_desk.h"
namespace esphome {
namespace maidesite_desk {
static const char* const TAG = "maidesite_desk";
namespace esphome
{
namespace maidesite_desk
{
static const char *const TAG = "maidesite_desk";
void MaidesiteDeskComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Maidesite Desk");
delay(10);
}
void MaidesiteDeskComponent::dump_config()
{
ESP_LOGCONFIG(TAG, "Maidesite Desk");
void MaidesiteDeskComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Maidesite Desk ...");
delay(10);
this->check_uart_settings(9600);
this->request_physical_limits();
this->request_limits();
this->request_settings();
}
void MaidesiteDeskComponent::loop() {
// This is a very simple method of receiving messages from the desk.
// It will fail on messages that contain the value 0x7E in their payload.
// But it is super simple and works for the messages we care about.
// Read message:
// format: 0xF2 0xF2 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
uint8_t oneByte;
while (this->available()) {
this->read_byte(&oneByte);
// Message shall start with 0xF2, if not skip this byte
if (!this->response_receiving_) {
if (oneByte != 0xF2)
continue;
this->response_receiving_ = true;
}
// Add current byte to message buffer
this->response_message_.push_back(oneByte);
// Discard message if 2. byte is not 0xF2
if (this->response_message_.size() == 2 && oneByte != 0xF2) {
ESP_LOGW(TAG, "Invalid response message: 2. byte is 0x%02X but expexted is 0xF2", oneByte);
delay(10);
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
void MaidesiteDeskComponent::setup()
{
ESP_LOGCONFIG(TAG, "Setting up Maidesite Desk ...");
// Discard message if it is too long
if (this->response_message_.size() > 10) {
ESP_LOGW(TAG, "Response message too long: expected not more than 10 bytes");
delay(10);
this->check_uart_settings(9600);
this->request_physical_limits();
this->request_limits();
this->request_settings();
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
void MaidesiteDeskComponent::loop()
{
// This is a very simple method of receiving messages from the desk.
// It will fail on messages that contain the value 0x7E in their payload.
// But it is super simple and works for the messages we care about.
// Read message:
// format: 0xF2 0xF2 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
uint8_t oneByte;
while (this->available())
{
this->read_byte(&oneByte);
// Message shall start with 0xF2, if not skip this byte
if (!this->response_receiving_)
{
if (oneByte != 0xF2)
continue;
this->response_receiving_ = true;
}
// Add current byte to message buffer
this->response_message_.push_back(oneByte);
// Discard message if 2. byte is not 0xF2
if (this->response_message_.size() == 2 && oneByte != 0xF2)
{
ESP_LOGW(TAG, "Invalid response message: 2. byte is 0x%02X but expexted is 0xF2", oneByte);
delay(10);
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
// Discard message if it is too long
if (this->response_message_.size() > 10)
{
ESP_LOGW(TAG, "Response message too long: expected not more than 10 bytes");
delay(10);
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
// Decode message if message is complete (last byte shall be 0x7E)
if (oneByte == 0x7E)
{
this->log_uart_hex("<<<", this->response_message_, ',');
this->decode_response(this->response_message_);
this->response_message_.clear();
this->response_receiving_ = false;
}
}
// Decode message if message is complete (last byte shall be 0x7E)
if (oneByte == 0x7E) {
this->log_uart_hex("<<<", this->response_message_, ',');
this->decode_response(this->response_message_);
this->response_message_.clear();
this->response_receiving_ = false;
}
}
}
void MaidesiteDeskComponent::log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator)
{
if (this->log_uart_msg_ == false) return;
void MaidesiteDeskComponent::log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator) {
if (this->log_uart_msg_ == false)
return;
std::string logStr;
char buffer[5];
std::string logStr;
char buffer[5];
for (size_t i = 0; i < bytes.size(); i++)
{
if (i > 0) logStr += separator;
sprintf(buffer, "%02X", bytes[i]);
logStr += buffer;
}
for (size_t i = 0; i < logStr.length(); i += UART_LOG_CHUNK_SIZE)
{
ESP_LOGI(TAG, "%s %s", prefix.c_str(), logStr.substr(i, UART_LOG_CHUNK_SIZE).c_str());
delay(10);
}
for (size_t i = 0; i < bytes.size(); i++) {
if (i > 0)
logStr += separator;
sprintf(buffer, "%02X", bytes[i]);
logStr += buffer;
}
for (size_t i = 0; i < logStr.length(); i += UART_LOG_CHUNK_SIZE) {
ESP_LOGI(TAG, "%s %s", prefix.c_str(), logStr.substr(i, UART_LOG_CHUNK_SIZE).c_str());
delay(10);
}
}
void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
// ToDo: Add checksum check
switch (message[2]) {
case 0x01:
ESP_LOGI(TAG, "Set height to 0x%0X%0X", message[4], message[5]);
delay(10);
float new_height;
new_height = byte2float(message[4], message[5]);
if (new_height == this->current_height_)
return;
this->current_height_ = new_height;
if (this->height_abs_sensor_ != nullptr)
this->height_abs_sensor_->publish_state(this->current_height_);
if (this->height_abs_number_ != nullptr)
this->height_abs_number_->publish_state(this->current_height_);
if (this->height_pct_sensor_ != nullptr && limit_max_ != 0)
this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) /
(this->limit_max_ - this->limit_min_) * 100);
if (this->height_pct_number_ != nullptr && limit_max_ != 0)
this->height_pct_number_->publish_state(
roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10);
break;
case 0x20:
ESP_LOGI(TAG, "Set limits to 0x%0X max=%i min=%i", message[4], (message[5] & 1), (message[4] >> 4));
delay(10);
if ((message[4] & 1) == 0) { // low nibble 0 -> no max limit, use physical_max_
this->limit_max_ = this->physical_max_;
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(this->limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(this->limit_max_);
}
void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message)
{
// ToDo: Add checksum check
switch (message[2])
{
case 0x01:
ESP_LOGI(TAG, "Set height to 0x%0X%0X", message[4], message[5]);
delay(10);
float new_height;
new_height = byte2float(message[4], message[5]);
if (new_height == this->current_height_)
return;
this->current_height_ = new_height;
if (this->height_abs_sensor_ != nullptr)
this->height_abs_sensor_->publish_state(this->current_height_);
if (this->height_abs_number_ != nullptr)
this->height_abs_number_->publish_state(this->current_height_);
if (this->height_pct_sensor_ != nullptr && limit_max_ != 0)
this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 100);
if (this->height_pct_number_ != nullptr && limit_max_ != 0)
this->height_pct_number_->publish_state(roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10);
break;
case 0x20:
ESP_LOGI(TAG, "Set limits to 0x%0X max=%i min=%i", message[4], (message[5] & 1), (message[4] >> 4));
delay(10);
if ((message[4] & 1) == 0)
{ // low nibble 0 -> no max limit, use physical_max_
this->limit_max_ = this->physical_max_;
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(this->limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(this->limit_max_);
}
if ((message[4] >> 4) == 0)
{ // high nibble 0 -> no min limit, use physical_min_
this->limit_min_ = this->physical_min_;
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
}
break;
case 0x07:
ESP_LOGI(TAG, "Set physical limits to min=0x%02X%02X max=0x%02X%02X", message[4], message[5], message[6], message[7]);
delay(10);
this->physical_max_ = this->byte2float(message[4], message[5]);
this->physical_min_ = this->byte2float(message[6], message[7]);
break;
case 0x21:
ESP_LOGI(TAG, "Set max. height to 0x%02X%02X", message[4], message[5]);
delay(10);
this->limit_max_ = this->byte2float(message[4], message[5]);
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(limit_max_);
break;
case 0x22:
ESP_LOGI(TAG, "Set min. height to 0x%02X%02X", message[4], message[5]);
delay(10);
this->limit_min_ = this->byte2float(message[4], message[5]);
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
break;
case 0x25:
ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m1_sensor_ != nullptr)
this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x26:
ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m2_sensor_ != nullptr)
this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x27:
ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m3_sensor_ != nullptr)
this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x28:
ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m4_sensor_ != nullptr)
this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
// case 0x0E:
// ESP_LOGI(TAG, "units 0x%02X", message[4]);
// if (units != nullptr)
// units->publish_state(byte2float(message[4], message[5]));
// break;
default:
ESP_LOGI(TAG, "Received unknown message");
delay(10);
}
if ((message[4] >> 4) == 0) { // high nibble 0 -> no min limit, use physical_min_
this->limit_min_ = this->physical_min_;
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
}
break;
float MaidesiteDeskComponent::byte2float(int high, int low)
{
return static_cast<float>((high << 8) + low) / 10;
}
case 0x07:
ESP_LOGI(TAG, "Set physical limits to min=0x%02X%02X max=0x%02X%02X", message[4], message[5], message[6],
message[7]);
delay(10);
// Write message:
// format: 0xF1 0xF1 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
void MaidesiteDeskComponent::send_simple_command(unsigned char cmd)
{
this->request_message_.clear();
this->request_message_.insert(this->request_message_.end(), { 0xF1, 0xF1, cmd, 0x00, cmd, 0x7E });
this->write_array(this->request_message_);
this->log_uart_hex(">>>", this->request_message_, ',');
delay(100); // NOLINT
}
this->physical_max_ = this->byte2float(message[4], message[5]);
this->physical_min_ = this->byte2float(message[6], message[7]);
break;
void MaidesiteDeskComponent::send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte)
{
// ToDo: make one function of send_simple_command and send_2byte_command
case 0x21:
ESP_LOGI(TAG, "Set max. height to 0x%02X%02X", message[4], message[5]);
delay(10);
unsigned char checksum = cmd + 2 + high_byte + low_byte;
this->request_message_.clear();
this->request_message_.insert(this->request_message_.end(), { 0xF1, 0xF1, cmd, 0x02, high_byte, low_byte, checksum, 0x7E });
this->write_array(this->request_message_);
this->log_uart_hex(">>>", this->request_message_, ',');
delay(100); // NOLINT
}
this->limit_max_ = this->byte2float(message[4], message[5]);
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(limit_max_);
break;
void MaidesiteDeskComponent::step_up()
{
this->send_simple_command(0x01);
}
case 0x22:
ESP_LOGI(TAG, "Set min. height to 0x%02X%02X", message[4], message[5]);
delay(10);
void MaidesiteDeskComponent::step_down()
{
this->send_simple_command(0x02);
}
this->limit_min_ = this->byte2float(message[4], message[5]);
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
break;
void MaidesiteDeskComponent::stop()
{
this->send_simple_command(0x2B);
}
case 0x25:
ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]);
delay(10);
void MaidesiteDeskComponent::goto_mem_position(int pos)
{
switch (pos)
{
case 1:
this->send_simple_command(0x05);
break;
case 2:
this->send_simple_command(0x06);
break;
case 3:
this->send_simple_command(0x27);
break;
case 4:
this->send_simple_command(0x28);
break;
}
}
if (this->position_m1_sensor_ != nullptr)
this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
void MaidesiteDeskComponent::save_mem_position(int pos)
{
switch (pos)
{
case 1:
this->send_simple_command(0x03);
break;
case 2:
this->send_simple_command(0x04);
break;
case 3:
this->send_simple_command(0x25);
break;
case 4:
this->send_simple_command(0x26);
break;
}
}
case 0x26:
ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]);
delay(10);
void MaidesiteDeskComponent::request_physical_limits()
{
this->send_simple_command(0x0C);
}
if (this->position_m2_sensor_ != nullptr)
this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
void MaidesiteDeskComponent::request_limits()
{
this->send_simple_command(0x20);
}
case 0x27:
ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]);
delay(10);
void MaidesiteDeskComponent::request_settings()
{
this->send_simple_command(0x07);
}
if (this->position_m3_sensor_ != nullptr)
this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
void MaidesiteDeskComponent::request_move_to()
{
this->send_simple_command(0x1B);
}
case 0x28:
ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]);
delay(10);
void MaidesiteDeskComponent::goto_max_position()
{
this->goto_height(limit_max_);
}
if (this->position_m4_sensor_ != nullptr)
this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
void MaidesiteDeskComponent::goto_min_position()
{
this->goto_height(limit_min_);
}
// case 0x0E:
// ESP_LOGI(TAG, "units 0x%02X", message[4]);
// if (units != nullptr)
// units->publish_state(byte2float(message[4], message[5]));
// break;
void MaidesiteDeskComponent::goto_height(float height)
{
unsigned char high_byte = ((int)height * 10) >> 8;
unsigned char low_byte = ((int)height * 10) & 0xFF;
default:
ESP_LOGI(TAG, "Received unknown message");
delay(10);
}
}
this->send_2byte_command(0x80, high_byte, low_byte);
this->request_move_to();
}
float MaidesiteDeskComponent::byte2float(int high, int low) {
return static_cast<float>((high << 8) + low) / 10;
}
// Write message:
// format: 0xF1 0xF1 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
void MaidesiteDeskComponent::send_simple_command(unsigned char cmd) {
this->request_message_.clear();
this->request_message_.insert(this->request_message_.end(), {0xF1, 0xF1, cmd, 0x00, cmd, 0x7E});
this->write_array(this->request_message_);
this->log_uart_hex(">>>", this->request_message_, ',');
delay(100); // NOLINT
}
void MaidesiteDeskComponent::send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte) {
// ToDo: make one function of send_simple_command and send_2byte_command
unsigned char checksum = cmd + 2 + high_byte + low_byte;
this->request_message_.clear();
this->request_message_.insert(this->request_message_.end(),
{0xF1, 0xF1, cmd, 0x02, high_byte, low_byte, checksum, 0x7E});
this->write_array(this->request_message_);
this->log_uart_hex(">>>", this->request_message_, ',');
delay(100); // NOLINT
}
void MaidesiteDeskComponent::step_up() {
this->send_simple_command(0x01);
}
void MaidesiteDeskComponent::step_down() {
this->send_simple_command(0x02);
}
void MaidesiteDeskComponent::stop() {
this->send_simple_command(0x2B);
}
void MaidesiteDeskComponent::goto_mem_position(int pos) {
switch (pos) {
case 1:
this->send_simple_command(0x05);
break;
case 2:
this->send_simple_command(0x06);
break;
case 3:
this->send_simple_command(0x27);
break;
case 4:
this->send_simple_command(0x28);
break;
}
}
void MaidesiteDeskComponent::save_mem_position(int pos) {
switch (pos) {
case 1:
this->send_simple_command(0x03);
break;
case 2:
this->send_simple_command(0x04);
break;
case 3:
this->send_simple_command(0x25);
break;
case 4:
this->send_simple_command(0x26);
break;
}
}
void MaidesiteDeskComponent::request_physical_limits() {
this->send_simple_command(0x0C);
}
void MaidesiteDeskComponent::request_limits() {
this->send_simple_command(0x20);
}
void MaidesiteDeskComponent::request_settings() {
this->send_simple_command(0x07);
}
void MaidesiteDeskComponent::request_move_to() {
this->send_simple_command(0x1B);
}
void MaidesiteDeskComponent::goto_max_position() {
this->goto_height(limit_max_);
}
void MaidesiteDeskComponent::goto_min_position() {
this->goto_height(limit_min_);
}
void MaidesiteDeskComponent::goto_height(float height) {
unsigned char high_byte = ((int)height * 10) >> 8;
unsigned char low_byte = ((int)height * 10) & 0xFF;
this->send_2byte_command(0x80, high_byte, low_byte);
this->request_move_to();
}
#ifdef USE_BUTTON
void MaidesiteDeskComponent::button_press_action(button::Button* object)
{
if (object == step_up_button_)
this->step_up();
else if (object == step_down_button_)
this->step_down();
else if (object == stop_button_)
this->stop();
else if (object == goto_max_button_)
this->goto_max_position();
else if (object == goto_min_button_)
this->goto_min_position();
else if (object == goto_m1_button_)
this->goto_mem_position(1);
else if (object == goto_m2_button_)
this->goto_mem_position(2);
else if (object == goto_m3_button_)
this->goto_mem_position(3);
else if (object == goto_m4_button_)
this->goto_mem_position(4);
else if (object == save_m1_button_)
{
this->save_mem_position(1);
this->request_settings();
}
else if (object == save_m2_button_)
{
this->save_mem_position(2);
this->request_settings();
}
else if (object == save_m3_button_)
{
this->save_mem_position(3);
this->request_settings();
}
else if (object == save_m4_button_)
{
this->save_mem_position(4);
this->request_settings();
}
}
void MaidesiteDeskComponent::button_press_action(button::Button* object) {
if (object == step_up_button_)
this->step_up();
else if (object == step_down_button_)
this->step_down();
else if (object == stop_button_)
this->stop();
else if (object == goto_max_button_)
this->goto_max_position();
else if (object == goto_min_button_)
this->goto_min_position();
else if (object == goto_m1_button_)
this->goto_mem_position(1);
else if (object == goto_m2_button_)
this->goto_mem_position(2);
else if (object == goto_m3_button_)
this->goto_mem_position(3);
else if (object == goto_m4_button_)
this->goto_mem_position(4);
else if (object == save_m1_button_) {
this->save_mem_position(1);
this->request_settings();
} else if (object == save_m2_button_) {
this->save_mem_position(2);
this->request_settings();
} else if (object == save_m3_button_) {
this->save_mem_position(3);
this->request_settings();
} else if (object == save_m4_button_) {
this->save_mem_position(4);
this->request_settings();
}
}
#endif
#ifdef USE_NUMBER
void MaidesiteDeskComponent::number_control(number::Number* object, float value)
{
if (object == height_abs_number_)
this->goto_height(value);
else if (object == height_pct_number_)
if (limit_max_ > 0 && limit_max_ > limit_min_)
this->goto_height((limit_max_ - limit_min_) * value / 100 + limit_min_);
}
void MaidesiteDeskComponent::number_control(number::Number* object, float value) {
if (object == height_abs_number_)
this->goto_height(value);
else if (object == height_pct_number_)
if (limit_max_ > 0 && limit_max_ > limit_min_)
this->goto_height((limit_max_ - limit_min_) * value / 100 + limit_min_);
}
#endif
} // namespace maidesite_desk
} // namespace esphome
} // namespace maidesite_desk
} // namespace esphome

View File

@ -17,98 +17,98 @@
#define UART_LOG_CHUNK_SIZE 153
namespace esphome {
namespace maidesite_desk {
namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskComponent : public Component, public uart::UARTDevice
{
public:
class MaidesiteDeskComponent : public Component, public uart::UARTDevice {
public:
#ifdef USE_SENSOR
SUB_SENSOR(unit);
SUB_SENSOR(height_abs);
SUB_SENSOR(height_pct);
SUB_SENSOR(height_min);
SUB_SENSOR(height_max);
SUB_SENSOR(position_m1);
SUB_SENSOR(position_m2);
SUB_SENSOR(position_m3);
SUB_SENSOR(position_m4);
SUB_SENSOR(unit);
SUB_SENSOR(height_abs);
SUB_SENSOR(height_pct);
SUB_SENSOR(height_min);
SUB_SENSOR(height_max);
SUB_SENSOR(position_m1);
SUB_SENSOR(position_m2);
SUB_SENSOR(position_m3);
SUB_SENSOR(position_m4);
#endif
#ifdef USE_BUTTON
SUB_BUTTON(unit);
SUB_BUTTON(step_up);
SUB_BUTTON(step_down);
SUB_BUTTON(stop);
SUB_BUTTON(goto_max);
SUB_BUTTON(goto_min);
SUB_BUTTON(goto_m1);
SUB_BUTTON(goto_m2);
SUB_BUTTON(goto_m3);
SUB_BUTTON(goto_m4);
SUB_BUTTON(save_m1);
SUB_BUTTON(save_m2);
SUB_BUTTON(save_m3);
SUB_BUTTON(save_m4);
SUB_BUTTON(unit);
SUB_BUTTON(step_up);
SUB_BUTTON(step_down);
SUB_BUTTON(stop);
SUB_BUTTON(goto_max);
SUB_BUTTON(goto_min);
SUB_BUTTON(goto_m1);
SUB_BUTTON(goto_m2);
SUB_BUTTON(goto_m3);
SUB_BUTTON(goto_m4);
SUB_BUTTON(save_m1);
SUB_BUTTON(save_m2);
SUB_BUTTON(save_m3);
SUB_BUTTON(save_m4);
void button_press_action(button::Button* object);
void button_press_action(button::Button* object);
#endif
#ifdef USE_NUMBER
SUB_NUMBER(height_abs);
SUB_NUMBER(height_pct);
SUB_NUMBER(height_abs);
SUB_NUMBER(height_pct);
void number_control(number::Number* object, float value);
void number_control(number::Number* object, float value);
#endif
MaidesiteDeskComponent() = default;
// base class functions
float get_setup_priority() const override { return setup_priority::LATE; }
void dump_config() override;
void setup() override;
void loop() override;
// option functions
void set_log_uart_msg(bool enable) { this->log_uart_msg_ = enable; }
// functions to use in lambdas
void request_physical_limits();
void request_limits();
void request_settings();
void request_move_to();
void step_up();
void step_down();
void stop();
void goto_height(float height);
void goto_min_position();
void goto_max_position();
void goto_mem_position(int pos);
void save_mem_position(int pos);
MaidesiteDeskComponent() = default;
// base class functions
float get_setup_priority() const override {
return setup_priority::LATE;
}
void dump_config() override;
void setup() override;
void loop() override;
// option functions
void set_log_uart_msg(bool enable) {
this->log_uart_msg_ = enable;
}
// functions to use in lambdas
void request_physical_limits();
void request_limits();
void request_settings();
void request_move_to();
void step_up();
void step_down();
void stop();
void goto_height(float height);
void goto_min_position();
void goto_max_position();
void goto_mem_position(int pos);
void save_mem_position(int pos);
// variables to use in lambdas
std::vector<uint8_t> response_message_;
// variables to use in lambdas
std::vector<uint8_t> response_message_;
protected:
// uart message functions
void decode_response(std::vector<uint8_t> message);
void send_simple_command(unsigned char cmd);
void send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte);
float byte2float(int high, int low);
void log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator);
protected:
// uart message functions
void decode_response(std::vector<uint8_t> message);
void send_simple_command(unsigned char cmd);
void send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte);
float byte2float(int high, int low);
void log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator);
// options variables
bool log_uart_msg_ { false };
// uart message variables
std::vector<uint8_t> request_message_;
uint8_t response_payload_length_;
uint8_t request_payload_length_;
bool response_receiving_ { false };
bool request_receiving_ { false };
// sensor variables
float current_height_ = 0;
float limit_min_ = 0;
float limit_max_ = 0;
float physical_min_ = 0;
float physical_max_ = 0;
};
} // namespace maidesite_desk
} // namespace esphome
// options variables
bool log_uart_msg_{false};
// uart message variables
std::vector<uint8_t> request_message_;
uint8_t response_payload_length_;
uint8_t request_payload_length_;
bool response_receiving_{false};
bool request_receiving_{false};
// sensor variables
float current_height_ = 0;
float limit_min_ = 0;
float limit_max_ = 0;
float physical_min_ = 0;
float physical_max_ = 0;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -2,53 +2,62 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import number
from esphome.const import (
UNIT_CENTIMETER,
UNIT_PERCENT,
UNIT_CENTIMETER,
UNIT_PERCENT,
)
from .. import CONF_MAIDESITE_DESK_ID, MaidesiteDeskComponent, maidesite_desk_ns
CONF_HEIGHT_ABS = "height_abs"
CONF_HEIGHT_PCT = "height_pct"
TYPES = [
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
]
CONF_NUMBERS = [
[ 0, 120, .2, ],
[ 0, 100, .1, ],
[
0,
120,
0.2,
],
[
0,
100,
0.1,
],
]
MaidesiteDeskNumber = maidesite_desk_ns.class_("MaidesiteDeskNumber", number.Number, cg.Component)
MaidesiteDeskNumber = maidesite_desk_ns.class_(
"MaidesiteDeskNumber", number.Number, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_HEIGHT_ABS): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_PCT): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_PERCENT,
),
}
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_HEIGHT_ABS): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_PCT): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_PERCENT,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await number.new_number(
child_config,
min_value=CONF_NUMBERS[index][0],
max_value=CONF_NUMBERS[index][1],
step=CONF_NUMBERS[index][2]
)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_number")(var))
cg.add(var.set_id(index))
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await number.new_number(
child_config,
min_value=CONF_NUMBERS[index][0],
max_value=CONF_NUMBERS[index][1],
step=CONF_NUMBERS[index][2],
)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_number")(var))
cg.add(var.set_id(index))

View File

@ -1,23 +1,18 @@
#include "maidesite_desk_number.h"
#include "esphome/core/log.h"
namespace esphome {
namespace maidesite_desk {
static const char* const TAG = "maidesite_desk.number";
namespace esphome
{
namespace maidesite_desk
{
static const char *const TAG = "maidesite_desk.number";
void MaidesiteDeskNumber::dump_config() {
LOG_NUMBER("", "Maidesite Desk Number", this);
delay(10);
}
void MaidesiteDeskNumber::dump_config()
{
LOG_NUMBER("", "Maidesite Desk Number", this);
delay(10);
}
void MaidesiteDeskNumber::control(float value)
{
this->publish_state(value);
this->parent_->number_control(this, value);
}
} // namespace maidesite_desk
} // namespace esphome
void MaidesiteDeskNumber::control(float value) {
this->publish_state(value);
this->parent_->number_control(this, value);
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -3,21 +3,19 @@
#include "esphome/core/component.h"
#include "../maidesite_desk.h"
namespace esphome {
namespace maidesite_desk {
class MaidesiteDeskNumber : public number::Number, public Component, public Parented<MaidesiteDeskComponent> {
public:
MaidesiteDeskNumber() = default;
void dump_config() override;
void set_id(int id) {
this->id_ = id;
}
namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskNumber : public number::Number, public Component, public Parented<MaidesiteDeskComponent>
{
public:
MaidesiteDeskNumber() = default;
void dump_config() override;
void set_id(int id) { this->id_ = id; }
protected:
void control(float value) override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome
protected:
void control(float value) override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -2,8 +2,8 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
UNIT_CENTIMETER,
UNIT_PERCENT,
UNIT_CENTIMETER,
UNIT_PERCENT,
)
from .. import CONF_MAIDESITE_DESK_ID, MaidesiteDeskComponent, maidesite_desk_ns
@ -18,73 +18,74 @@ CONF_POSITION_M3 = "position_m3"
CONF_POSITION_M4 = "position_m4"
TYPES = [
CONF_UNIT,
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
CONF_HEIGHT_MIN,
CONF_HEIGHT_MAX,
CONF_POSITION_M1,
CONF_POSITION_M2,
CONF_POSITION_M3,
CONF_POSITION_M4,
CONF_UNIT,
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
CONF_HEIGHT_MIN,
CONF_HEIGHT_MAX,
CONF_POSITION_M1,
CONF_POSITION_M2,
CONF_POSITION_M3,
CONF_POSITION_M4,
]
MaidesiteDeskSensor = maidesite_desk_ns.class_("MaidesiteDeskSensor", sensor.Sensor, cg.Component)
MaidesiteDeskSensor = maidesite_desk_ns.class_(
"MaidesiteDeskSensor", sensor.Sensor, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_UNIT): sensor.sensor_schema(),
cv.Optional(CONF_HEIGHT_ABS): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_PERCENT,
),
cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M1): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M2): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M3): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M4): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
}
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_UNIT): sensor.sensor_schema(),
cv.Optional(CONF_HEIGHT_ABS): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_PERCENT,
),
cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M1): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M2): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M3): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M4): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
unit_of_measurement=UNIT_CENTIMETER,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for key in TYPES:
if child_config := config.get(key):
var = await sensor.new_sensor(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_sensor")(var))
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for key in TYPES:
if child_config := config.get(key):
var = await sensor.new_sensor(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_sensor")(var))

View File

@ -1,17 +1,13 @@
#include "maidesite_desk_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace maidesite_desk {
static const char* const TAG = "maidesite_desk.sensor";
namespace esphome
{
namespace maidesite_desk
{
static const char *const TAG = "maidesite_desk.sensor";
void MaidesiteDeskSensor::dump_config()
{
LOG_SENSOR("", "Maidesite Desk Sensor", this);
delay(10);
}
} // namespace maidesite_desk
} // namespace esphome
void MaidesiteDeskSensor::dump_config() {
LOG_SENSOR("", "Maidesite Desk Sensor", this);
delay(10);
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -3,16 +3,12 @@
#include "esphome/core/component.h"
#include "../maidesite_desk.h"
namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskSensor : public sensor::Sensor, public Component, public Parented<MaidesiteDeskComponent>
{
public:
MaidesiteDeskSensor() = default;
void dump_config() override;
};
} // namespace maidesite_desk
} // namespace esphome
namespace esphome {
namespace maidesite_desk {
class MaidesiteDeskSensor : public sensor::Sensor, public Component, public Parented<MaidesiteDeskComponent> {
public:
MaidesiteDeskSensor() = default;
void dump_config() override;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -3,39 +3,39 @@ import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import CONF_ID
CODEOWNERS = ['@elvit']
CODEOWNERS = ["@elvit"]
DEPENDENCIES = ["uart"]
MULTICONF = True
DEPENDENCIES = ['uart']
CONF_PANASONIC_HEATPUMP_ID = "panasonic_heatpump"
CONF_UART_CLIENT = "uart_client_id"
CONF_LOG_UART_MSG = "log_uart_msg"
panasonic_heatpump_ns = cg.esphome_ns.namespace('panasonic_heatpump')
PanasonicHeatpumpComponent = panasonic_heatpump_ns.class_('PanasonicHeatpumpComponent', cg.PollingComponent, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(PanasonicHeatpumpComponent),
cv.Optional(CONF_UART_CLIENT): cv.use_id(uart.UARTComponent),
cv.Optional(CONF_LOG_UART_MSG, default=False): cv.boolean,
}
)
.extend(cv.polling_component_schema("3s"))
.extend(uart.UART_DEVICE_SCHEMA)
panasonic_heatpump_ns = cg.esphome_ns.namespace("panasonic_heatpump")
PanasonicHeatpumpComponent = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpComponent", cg.PollingComponent, uart.UARTDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(PanasonicHeatpumpComponent),
cv.Optional(CONF_UART_CLIENT): cv.use_id(uart.UARTComponent),
cv.Optional(CONF_LOG_UART_MSG, default=False): cv.boolean,
}
)
.extend(cv.polling_component_schema("3s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
if CONF_UART_CLIENT in config:
uart_client = await cg.get_variable(config[CONF_UART_CLIENT])
cg.add(var.set_uart_client(uart_client))
if CONF_UART_CLIENT in config:
uart_client = await cg.get_variable(config[CONF_UART_CLIENT])
cg.add(var.set_uart_client(uart_client))
cg.add(var.set_log_uart_msg(config[CONF_LOG_UART_MSG]))
cg.add(var.set_log_uart_msg(config[CONF_LOG_UART_MSG]))

View File

@ -2,10 +2,13 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_RUNNING,
)
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
CONF_TOP0 = "top0" # Heatpump State
CONF_TOP2 = "top2" # Force DHW State
@ -32,125 +35,129 @@ CONF_TOP132 = "top132" # Bivalent Advanced Heat
CONF_TOP133 = "top133" # Bivalent Advanced DHW
TYPES = [
CONF_TOP0,
CONF_TOP2,
CONF_TOP3,
CONF_TOP13,
CONF_TOP26,
CONF_TOP60,
CONF_TOP61,
CONF_TOP68,
CONF_TOP69,
CONF_TOP99,
CONF_TOP100,
CONF_TOP108,
CONF_TOP109,
CONF_TOP110,
CONF_TOP119,
CONF_TOP120,
CONF_TOP121,
CONF_TOP122,
CONF_TOP123,
CONF_TOP124,
CONF_TOP129,
CONF_TOP132,
CONF_TOP133,
CONF_TOP0,
CONF_TOP2,
CONF_TOP3,
CONF_TOP13,
CONF_TOP26,
CONF_TOP60,
CONF_TOP61,
CONF_TOP68,
CONF_TOP69,
CONF_TOP99,
CONF_TOP100,
CONF_TOP108,
CONF_TOP109,
CONF_TOP110,
CONF_TOP119,
CONF_TOP120,
CONF_TOP121,
CONF_TOP122,
CONF_TOP123,
CONF_TOP124,
CONF_TOP129,
CONF_TOP132,
CONF_TOP133,
]
PanasonicHeatpumpBinarySensor = panasonic_heatpump_ns.class_("PanasonicHeatpumpBinarySensor", binary_sensor.BinarySensor, cg.Component)
PanasonicHeatpumpBinarySensor = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpBinarySensor", binary_sensor.BinarySensor, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(PanasonicHeatpumpComponent),
cv.Optional(CONF_TOP0): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP2): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP3): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP13): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP26): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP60): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP61): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP68): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP69): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP99): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP100): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP108): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP109): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class = DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP110): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP119): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP120): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP121): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP122): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP123): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP124): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP129): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP132): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP133): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_TOP0): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP2): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP3): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP13): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP26): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP60): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP61): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP68): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP69): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP99): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP100): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP108): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP109): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
device_class=DEVICE_CLASS_RUNNING,
),
cv.Optional(CONF_TOP110): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP119): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP120): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP121): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP122): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP123): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP124): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP129): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP132): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
cv.Optional(CONF_TOP133): binary_sensor.binary_sensor_schema(
PanasonicHeatpumpBinarySensor,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await binary_sensor.new_binary_sensor(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_binary_sensor(var))
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await binary_sensor.new_binary_sensor(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_binary_sensor(var))

View File

@ -1,121 +1,140 @@
#include "panasonic_heatpump_binary_sensor.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.binary_sensor";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.binary_sensor";
void PanasonicHeatpumpBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "Panasonic Heatpump Binary Sensor", this);
delay(10);
}
void PanasonicHeatpumpBinarySensor::dump_config()
{
LOG_BINARY_SENSOR("", "Panasonic Heatpump Binary Sensor", this);
delay(10);
}
void PanasonicHeatpumpBinarySensor::publish_new_state(const std::vector<uint8_t>& data) {
if (data.empty())
return;
void PanasonicHeatpumpBinarySensor::publish_new_state(const std::vector<uint8_t>& data)
{
if (data.empty()) return;
bool new_state;
switch (this->id_) {
case BinarySensorIds::CONF_TOP0:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[4]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP2:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[4]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP3:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[7]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP13:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[5]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP26:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[111]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP60:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[112]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP61:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[112]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP68:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[5]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP69:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[117]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP99:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[24]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP100:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[24]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP108:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[20]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP109:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[20]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP110:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[20]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP119:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[23]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP120:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[23]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP121:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[23]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP122:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[23]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP123:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[116]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP124:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[116]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP129:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[26]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP132:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[26]));
if (this->has_state() && this->state == new_state)
return;
break;
case BinarySensorIds::CONF_TOP133:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[26]));
if (this->has_state() && this->state == new_state)
return;
break;
default:
return;
};
bool new_state;
switch (this->id_)
{
case BinarySensorIds::CONF_TOP0:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[4]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP2:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[4]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP3:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[7]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP13:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[5]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP26:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[111]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP60:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[112]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP61:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[112]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP68:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[5]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP69:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[117]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP99:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[24]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP100:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[24]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP108:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[20]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP109:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[20]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP110:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[20]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP119:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[23]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP120:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[23]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP121:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[23]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP122:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[23]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP123:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[116]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP124:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[116]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP129:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[26]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP132:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[26]));
if (this->has_state() && this->state == new_state) return;
break;
case BinarySensorIds::CONF_TOP133:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[26]));
if (this->has_state() && this->state == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -4,45 +4,42 @@
#include "../panasonic_heatpump.h"
#include "../decode.h"
namespace esphome {
namespace panasonic_heatpump {
enum BinarySensorIds : uint8_t {
CONF_TOP0,
CONF_TOP2,
CONF_TOP3,
CONF_TOP13,
CONF_TOP26,
CONF_TOP60,
CONF_TOP61,
CONF_TOP68,
CONF_TOP69,
CONF_TOP99,
CONF_TOP100,
CONF_TOP108,
CONF_TOP109,
CONF_TOP110,
CONF_TOP119,
CONF_TOP120,
CONF_TOP121,
CONF_TOP122,
CONF_TOP123,
CONF_TOP124,
CONF_TOP129,
CONF_TOP132,
CONF_TOP133,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum BinarySensorIds : uint8_t
{
CONF_TOP0,
CONF_TOP2,
CONF_TOP3,
CONF_TOP13,
CONF_TOP26,
CONF_TOP60,
CONF_TOP61,
CONF_TOP68,
CONF_TOP69,
CONF_TOP99,
CONF_TOP100,
CONF_TOP108,
CONF_TOP109,
CONF_TOP110,
CONF_TOP119,
CONF_TOP120,
CONF_TOP121,
CONF_TOP122,
CONF_TOP123,
CONF_TOP124,
CONF_TOP129,
CONF_TOP132,
CONF_TOP133,
};
class PanasonicHeatpumpBinarySensor : public binary_sensor::BinarySensor, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpBinarySensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
class PanasonicHeatpumpBinarySensor : public binary_sensor::BinarySensor,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpBinarySensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,21 +1,16 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate
from esphome.components.climate import (
ClimateMode,
ClimatePreset,
)
from esphome.const import (
CONF_ID,
CONF_SUPPORTED_MODES,
CONF_SUPPORTED_PRESETS,
CONF_CUSTOM_PRESETS,
CONF_VISUAL,
CONF_MIN_TEMPERATURE,
CONF_MAX_TEMPERATURE,
CONF_TEMPERATURE_STEP,
CONF_MIN_TEMPERATURE,
CONF_MAX_TEMPERATURE,
CONF_TEMPERATURE_STEP,
)
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
CONF_COOL_MODE = "cool_mode"
CONF_CLIMATE_TANK = "tank"
@ -23,56 +18,56 @@ CONF_CLIMATE_ZONE1 = "zone1"
CONF_CLIMATE_ZONE2 = "zone2"
TYPES = [
CONF_CLIMATE_TANK,
CONF_CLIMATE_ZONE1,
CONF_CLIMATE_ZONE2,
CONF_CLIMATE_TANK,
CONF_CLIMATE_ZONE1,
CONF_CLIMATE_ZONE2,
]
def climate_options(min_temp, max_temp, temp_step) -> cv.Schema:
schema = cv.Schema({
cv.Optional(CONF_MIN_TEMPERATURE, default=min_temp): cv.float_,
cv.Optional(CONF_MAX_TEMPERATURE, default=max_temp): cv.float_,
cv.Optional(CONF_TEMPERATURE_STEP, default=temp_step): cv.float_,
})
return schema
PanasonicHeatpumpClimate = panasonic_heatpump_ns.class_("PanasonicHeatpumpClimate", climate.Climate, cg.Component)
def climate_options(min_temp, max_temp, temp_step) -> cv.Schema:
schema = cv.Schema(
{
cv.Optional(CONF_MIN_TEMPERATURE, default=min_temp): cv.float_,
cv.Optional(CONF_MAX_TEMPERATURE, default=max_temp): cv.float_,
cv.Optional(CONF_TEMPERATURE_STEP, default=temp_step): cv.float_,
}
)
return schema
PanasonicHeatpumpClimate = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpClimate", climate.Climate, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_COOL_MODE, default=False): cv.boolean,
cv.Optional(CONF_CLIMATE_TANK): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(
climate_options(20.0, 65.0, 0.5)
),
cv.Optional(CONF_CLIMATE_ZONE1): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(
climate_options(-5.0, 5.0, 0.5)
),
cv.Optional(CONF_CLIMATE_ZONE2): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(
climate_options(-5.0, 5.0, 0.5)
),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_COOL_MODE, default=False): cv.boolean,
cv.Optional(CONF_CLIMATE_TANK): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(climate_options(20.0, 65.0, 0.5)),
cv.Optional(CONF_CLIMATE_ZONE1): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(climate_options(-5.0, 5.0, 0.5)),
cv.Optional(CONF_CLIMATE_ZONE2): climate.climate_schema(
PanasonicHeatpumpClimate
).extend(climate_options(-5.0, 5.0, 0.5)),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await climate.new_climate(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(var.set_min_temperature(child_config[CONF_MIN_TEMPERATURE]))
cg.add(var.set_max_temperature(child_config[CONF_MAX_TEMPERATURE]))
cg.add(var.set_temperature_step(child_config[CONF_TEMPERATURE_STEP]))
cg.add(var.set_cool_mode(config[CONF_COOL_MODE]))
cg.add(parent.add_climate(var))
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await climate.new_climate(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(var.set_min_temperature(child_config[CONF_MIN_TEMPERATURE]))
cg.add(var.set_max_temperature(child_config[CONF_MAX_TEMPERATURE]))
cg.add(var.set_temperature_step(child_config[CONF_TEMPERATURE_STEP]))
cg.add(var.set_cool_mode(config[CONF_COOL_MODE]))
cg.add(parent.add_climate(var))

View File

@ -1,228 +1,207 @@
#include "panasonic_heatpump_climate.h"
#include "esphome/core/log.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.climate";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.climate";
void PanasonicHeatpumpClimate::dump_config() {
LOG_CLIMATE("", "Panasonic Heatpump Climate", this);
}
void PanasonicHeatpumpClimate::dump_config()
{
LOG_CLIMATE("", "Panasonic Heatpump Climate", this);
climate::ClimateTraits PanasonicHeatpumpClimate::traits() {
auto traits = climate::ClimateTraits();
// traits.set_supports_action(true);
traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE);
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT});
if (this->cool_mode_ &&
(this->id_ == ClimateIds::CONF_CLIMATE_ZONE1 || this->id_ == ClimateIds::CONF_CLIMATE_ZONE2)) {
traits.add_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE);
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
traits.add_supported_mode(climate::CLIMATE_MODE_AUTO);
}
traits.set_visual_min_temperature(this->min_temperature_);
traits.set_visual_max_temperature(this->max_temperature_);
traits.set_visual_temperature_step(this->temperature_step_);
return traits;
}
void PanasonicHeatpumpClimate::control(const climate::ClimateCall& call) {
if (call.get_mode().has_value()) {
int byte6 = this->parent_->getResponseByte(6);
if (byte6 >= 0) {
climate::ClimateMode new_mode = *call.get_mode();
uint8_t newByte6 = this->setClimateMode(new_mode, (uint8_t)byte6);
this->parent_->set_command_byte(newByte6, 6);
}
}
climate::ClimateTraits PanasonicHeatpumpClimate::traits()
{
auto traits = climate::ClimateTraits();
if (call.get_target_temperature().has_value()) {
float new_temp = *call.get_target_temperature();
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_TANK:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 42); // set11
break;
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 38); // set5
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 40); // set7
break;
};
}
//traits.set_supports_action(true);
traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE);
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT});
if (this->cool_mode_ &&
(this->id_ == ClimateIds::CONF_CLIMATE_ZONE1 ||
this->id_ == ClimateIds::CONF_CLIMATE_ZONE2))
{
traits.add_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE);
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
traits.add_supported_mode(climate::CLIMATE_MODE_AUTO);
}
traits.set_visual_min_temperature(this->min_temperature_);
traits.set_visual_max_temperature(this->max_temperature_);
traits.set_visual_temperature_step(this->temperature_step_);
if (call.get_target_temperature_high().has_value()) {
float new_temp = *call.get_target_temperature_high();
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 38); // set5
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 40); // set7
break;
};
}
return traits;
}
if (call.get_target_temperature_low().has_value()) {
float new_temp = *call.get_target_temperature_low();
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 39); // set6
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 41); // set8
break;
};
}
void PanasonicHeatpumpClimate::control(const climate::ClimateCall &call)
{
if (call.get_mode().has_value())
{
int byte6 = this->parent_->getResponseByte(6);
if (byte6 >= 0)
{
climate::ClimateMode new_mode = *call.get_mode();
uint8_t newByte6 = this->setClimateMode(new_mode, (uint8_t)byte6);
this->parent_->set_command_byte(newByte6, 6);
}
}
this->publish_state();
this->keep_state_ = 2;
}
if (call.get_target_temperature().has_value())
{
float new_temp = *call.get_target_temperature();
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_TANK:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 42); // set11
break;
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 38); // set5
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 40); // set7
break;
};
}
void PanasonicHeatpumpClimate::publish_new_state(const std::vector<uint8_t>& data) {
if (this->keep_state_ > 0) {
this->keep_state_--;
return;
}
if (data.empty())
return;
if (call.get_target_temperature_high().has_value())
{
float new_temp = *call.get_target_temperature_high();
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 38); // set5
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 40); // set7
break;
};
}
uint8_t new_mode;
float new_target_temp_heat;
float new_target_temp_cool;
float new_current_temp;
if (call.get_target_temperature_low().has_value())
{
float new_temp = *call.get_target_temperature_low();
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_ZONE1:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 39); // set6
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(new_temp), 41); // set8
break;
};
}
new_mode = this->getClimateMode(data[6]); // set9
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_TANK:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[42]); // set11
new_current_temp = PanasonicDecode::getByteMinus128(data[141]); // top10
break;
case ClimateIds::CONF_CLIMATE_ZONE1:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[38]); // set5
new_target_temp_cool = PanasonicDecode::getByteMinus128(data[39]); // set6
new_current_temp = PanasonicDecode::getByteMinus128(data[139]); // top56
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[40]); // set7
new_target_temp_cool = PanasonicDecode::getByteMinus128(data[41]); // set8
new_current_temp = PanasonicDecode::getByteMinus128(data[140]); // top57
break;
default:
return;
};
this->publish_state();
this->keep_state_ = 2;
}
if (!this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE) &&
this->mode == new_mode && this->target_temperature == new_target_temp_heat &&
this->current_temperature == new_current_temp)
return;
if (this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE) &&
this->mode == new_mode && this->target_temperature_high == new_target_temp_heat &&
this->target_temperature_low == new_target_temp_cool && this->current_temperature == new_current_temp)
return;
void PanasonicHeatpumpClimate::publish_new_state(const std::vector<uint8_t>& data)
{
if (this->keep_state_ > 0)
{
this->keep_state_--;
return;
}
if (data.empty()) return;
if (new_mode != 255)
this->mode = (climate::ClimateMode)new_mode;
// this->action = climate::CLIMATE_ACTION_IDLE;
if (this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) {
this->target_temperature_high = new_target_temp_heat;
this->target_temperature_low = new_target_temp_cool;
} else {
this->target_temperature = new_target_temp_heat;
}
this->current_temperature = new_current_temp;
this->publish_state();
}
uint8_t new_mode;
float new_target_temp_heat;
float new_target_temp_cool;
float new_current_temp;
uint8_t PanasonicHeatpumpClimate::getClimateMode(const uint8_t input) {
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_TANK:
switch ((uint8_t)(input & 0b110000)) {
case 0b010000:
return climate::CLIMATE_MODE_OFF;
case 0b100000:
return climate::CLIMATE_MODE_HEAT;
default:
return 255;
};
case ClimateIds::CONF_CLIMATE_ZONE1:
case ClimateIds::CONF_CLIMATE_ZONE2:
switch ((uint8_t)(input & 0b1111)) {
case 0b0001:
return climate::CLIMATE_MODE_OFF;
case 0b0010:
return climate::CLIMATE_MODE_HEAT;
case 0b0011:
return climate::CLIMATE_MODE_COOL;
case 0b1000:
return climate::CLIMATE_MODE_AUTO;
case 0b1001:
return climate::CLIMATE_MODE_AUTO; // 0x19 = auto-heat
case 0b1010:
return climate::CLIMATE_MODE_AUTO; // 0x1A = auto-cool
default:
return 255;
};
default:
return 255;
};
}
new_mode = this->getClimateMode(data[6]); // set9
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_TANK:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[42]); // set11
new_current_temp = PanasonicDecode::getByteMinus128(data[141]); // top10
break;
case ClimateIds::CONF_CLIMATE_ZONE1:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[38]); // set5
new_target_temp_cool = PanasonicDecode::getByteMinus128(data[39]); // set6
new_current_temp = PanasonicDecode::getByteMinus128(data[139]); // top56
break;
case ClimateIds::CONF_CLIMATE_ZONE2:
new_target_temp_heat = PanasonicDecode::getByteMinus128(data[40]); // set7
new_target_temp_cool = PanasonicDecode::getByteMinus128(data[41]); // set8
new_current_temp = PanasonicDecode::getByteMinus128(data[140]); // top57
break;
default: return;
};
if (!this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE) &&
this->mode == new_mode &&
this->target_temperature == new_target_temp_heat &&
this->current_temperature == new_current_temp) return;
if (this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE) &&
this->mode == new_mode &&
this->target_temperature_high == new_target_temp_heat &&
this->target_temperature_low == new_target_temp_cool &&
this->current_temperature == new_current_temp) return;
if (new_mode != 255) this->mode = (climate::ClimateMode)new_mode;
//this->action = climate::CLIMATE_ACTION_IDLE;
if (this->get_traits().has_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE))
{
this->target_temperature_high = new_target_temp_heat;
this->target_temperature_low = new_target_temp_cool;
}
else
{
this->target_temperature = new_target_temp_heat;
}
this->current_temperature = new_current_temp;
this->publish_state();
}
uint8_t PanasonicHeatpumpClimate::getClimateMode(const uint8_t input)
{
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_TANK:
switch ((uint8_t)(input & 0b110000))
{
case 0b010000:
return climate::CLIMATE_MODE_OFF;
case 0b100000:
return climate::CLIMATE_MODE_HEAT;
default: return 255;
};
case ClimateIds::CONF_CLIMATE_ZONE1:
case ClimateIds::CONF_CLIMATE_ZONE2:
switch ((uint8_t)(input & 0b1111))
{
case 0b0001:
return climate::CLIMATE_MODE_OFF;
case 0b0010:
return climate::CLIMATE_MODE_HEAT;
case 0b0011:
return climate::CLIMATE_MODE_COOL;
case 0b1000:
return climate::CLIMATE_MODE_AUTO;
case 0b1001:
return climate::CLIMATE_MODE_AUTO; // 0x19 = auto-heat
case 0b1010:
return climate::CLIMATE_MODE_AUTO; // 0x1A = auto-cool
default: return 255;
};
default: return 255;
};
}
uint8_t PanasonicHeatpumpClimate::setClimateMode(const climate::ClimateMode mode, uint8_t byte)
{
uint8_t newByte = byte;
switch (this->id_)
{
case ClimateIds::CONF_CLIMATE_TANK:
newByte = newByte & 0b11001111;
switch (mode)
{
case climate::CLIMATE_MODE_OFF:
return newByte + 0b010000;
case climate::CLIMATE_MODE_HEAT:
return newByte + 0b100000;
default: return 0;
};
case ClimateIds::CONF_CLIMATE_ZONE1:
case ClimateIds::CONF_CLIMATE_ZONE2:
newByte = newByte & 0b11110000;
switch (mode)
{
case climate::CLIMATE_MODE_OFF:
return newByte + 0b0001;
case climate::CLIMATE_MODE_HEAT:
return newByte + 0b0010;
case climate::CLIMATE_MODE_COOL:
return newByte + 0b0011;
case climate::CLIMATE_MODE_AUTO:
return newByte + 0b1000;
default: return 0;
};
default: return 0;
};
}
} // namespace panasonic_heatpump
} // namespace esphome
uint8_t PanasonicHeatpumpClimate::setClimateMode(const climate::ClimateMode mode, uint8_t byte) {
uint8_t newByte = byte;
switch (this->id_) {
case ClimateIds::CONF_CLIMATE_TANK:
newByte = newByte & 0b11001111;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
return newByte + 0b010000;
case climate::CLIMATE_MODE_HEAT:
return newByte + 0b100000;
default:
return 0;
};
case ClimateIds::CONF_CLIMATE_ZONE1:
case ClimateIds::CONF_CLIMATE_ZONE2:
newByte = newByte & 0b11110000;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
return newByte + 0b0001;
case climate::CLIMATE_MODE_HEAT:
return newByte + 0b0010;
case climate::CLIMATE_MODE_COOL:
return newByte + 0b0011;
case climate::CLIMATE_MODE_AUTO:
return newByte + 0b1000;
default:
return 0;
};
default:
return 0;
};
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -6,41 +6,46 @@
#include "../commands.h"
#include <set>
namespace esphome {
namespace panasonic_heatpump {
enum ClimateIds : uint8_t {
CONF_CLIMATE_TANK,
CONF_CLIMATE_ZONE1,
CONF_CLIMATE_ZONE2,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum ClimateIds : uint8_t
{
CONF_CLIMATE_TANK,
CONF_CLIMATE_ZONE1,
CONF_CLIMATE_ZONE2,
};
class PanasonicHeatpumpClimate : public climate::Climate,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpClimate() = default;
void dump_config() override;
climate::ClimateTraits traits() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
class PanasonicHeatpumpClimate : public climate::Climate, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpClimate() = default;
void dump_config() override;
climate::ClimateTraits traits() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
void set_cool_mode(bool value) {
this->cool_mode_ = value;
}
void set_min_temperature(float value) {
this->min_temperature_ = value;
}
void set_max_temperature(float value) {
this->max_temperature_ = value;
}
void set_temperature_step(float value) {
this->temperature_step_ = value;
}
void set_cool_mode(bool value) { this->cool_mode_ = value; }
void set_min_temperature(float value) { this->min_temperature_ = value; }
void set_max_temperature(float value) { this->max_temperature_ = value; }
void set_temperature_step(float value) { this->temperature_step_ = value; }
protected:
void control(const climate::ClimateCall& call) override;
uint8_t getClimateMode(const uint8_t input);
uint8_t setClimateMode(const climate::ClimateMode mode, const uint8_t byte);
protected:
void control(const climate::ClimateCall &call) override;
uint8_t getClimateMode(const uint8_t input);
uint8_t setClimateMode(const climate::ClimateMode mode, const uint8_t byte);
bool cool_mode_ { false };
float min_temperature_ { -5.0 };
float max_temperature_ { 5.0 };
float temperature_step_ { 0.5 };
};
} // namespace panasonic_heatpump
} // namespace esphome
bool cool_mode_{false};
float min_temperature_{-5.0};
float max_temperature_{5.0};
float temperature_step_{0.5};
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,164 +1,145 @@
#include "commands.h"
namespace esphome {
namespace panasonic_heatpump {
const uint8_t PanasonicCommand::InitialRequest[INIT_REQUEST_SIZE] = {0x31, 0x05, 0x10, 0x01, 0x00, 0x00, 0x00, 0xB9};
const uint8_t PanasonicCommand::InitialResponse[INIT_RESPONSE_SIZE] = {
0x31, 0x30, 0x01, 0x10, 0x1E, 0x6C, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C};
const uint8_t PanasonicCommand::PollingMessage[DATA_MESSAGE_SIZE] = {
0x71, 0x6C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12};
const uint8_t PanasonicCommand::PollingExtraMessage[DATA_MESSAGE_SIZE] = {
0x71, 0x6C, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
const uint8_t PanasonicCommand::CommandMessage[DATA_MESSAGE_SIZE] = {
0xF1, 0x6C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92};
namespace esphome
{
namespace panasonic_heatpump
{
const uint8_t PanasonicCommand::InitialRequest[INIT_REQUEST_SIZE] = {
0x31, 0x05, 0x10, 0x01, 0x00, 0x00, 0x00, 0xB9
};
const uint8_t PanasonicCommand::InitialResponse[INIT_RESPONSE_SIZE] = {
0x31, 0x30, 0x01, 0x10, 0x1E, 0x6C, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C
};
const uint8_t PanasonicCommand::PollingMessage[DATA_MESSAGE_SIZE] = {
0x71, 0x6C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x12
};
const uint8_t PanasonicCommand::PollingExtraMessage[DATA_MESSAGE_SIZE] = {
0x71, 0x6C, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
const uint8_t PanasonicCommand::CommandMessage[DATA_MESSAGE_SIZE] = {
0xF1, 0x6C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x92
};
uint8_t PanasonicCommand::calcChecksum(std::vector<uint8_t>& data, int length) {
uint8_t checksum = 0;
for (int i = 0; i < length; i++) {
checksum += data[i];
}
checksum = (checksum ^ 0xFF) + 01;
return checksum;
}
uint8_t PanasonicCommand::calcChecksum(std::vector<uint8_t>& data, int length)
{
uint8_t checksum = 0;
for (int i = 0; i < length; i++)
{
checksum += data[i];
}
checksum = (checksum ^ 0xFF) + 01;
return checksum;
}
uint8_t PanasonicCommand::setMultiply2(size_t input) {
return input * 0b10;
}
uint8_t PanasonicCommand::setMultiply2(size_t input)
{
return input * 0b10;
}
uint8_t PanasonicCommand::setMultiply4(size_t input) {
return input * 0b100;
}
uint8_t PanasonicCommand::setMultiply4(size_t input)
{
return input * 0b100;
}
uint8_t PanasonicCommand::setPlus1Multiply4(size_t input) {
return (input + 1) * 0b100;
}
uint8_t PanasonicCommand::setPlus1Multiply4(size_t input)
{
return (input + 1) * 0b100;
}
uint8_t PanasonicCommand::setPlus1Multiply8(size_t input) {
return (input + 1) * 0b1000;
}
uint8_t PanasonicCommand::setPlus1Multiply8(size_t input)
{
return (input + 1) * 0b1000;
}
uint8_t PanasonicCommand::setPlus1Multiply16(size_t input) {
return (input + 1) * 0b10000;
}
uint8_t PanasonicCommand::setPlus1Multiply16(size_t input)
{
return (input + 1) * 0b10000;
}
uint8_t PanasonicCommand::setPlus1Multiply64(size_t input) {
return (input + 1) * 0b1000000;
}
uint8_t PanasonicCommand::setPlus1Multiply64(size_t input)
{
return (input + 1) * 0b1000000;
}
uint8_t PanasonicCommand::setPlus1(size_t input) {
return input + 1;
}
uint8_t PanasonicCommand::setPlus1(size_t input)
{
return input + 1;
}
uint8_t PanasonicCommand::setPlus128(size_t input) {
return input + 0b10000000;
}
uint8_t PanasonicCommand::setPlus128(size_t input)
{
return input + 0b10000000;
}
uint8_t PanasonicCommand::setOperationMode(size_t input) {
switch (input) {
case 0:
return 0b100001; // 0x21 = tank
case 1:
return 0b010010; // 0x12 = heat
case 2:
return 0b100010; // 0x22 = heat+tank
case 3:
return 0b010011; // 0x13 = cool
case 4:
return 0b100011; // 0x23 = cool+tank
case 5:
return 0b011000; // 0x18 = auto
case 6:
return 0b101000; // 0x28 = auto+tank
case 7:
return 0b011001; // 0x19 = auto-heat
case 8:
return 0b101001; // 0x29 = auto-heat+tank
case 9:
return 0b011010; // 0x1A = auto-cool
case 10:
return 0b101010; // 0x2A = auto-cool+tank
default:
return 0; // do nothing
}
}
uint8_t PanasonicCommand::setOperationMode(size_t input)
{
switch (input)
{
case 0: return 0b100001; // 0x21 = tank
case 1: return 0b010010; // 0x12 = heat
case 2: return 0b100010; // 0x22 = heat+tank
case 3: return 0b010011; // 0x13 = cool
case 4: return 0b100011; // 0x23 = cool+tank
case 5: return 0b011000; // 0x18 = auto
case 6: return 0b101000; // 0x28 = auto+tank
case 7: return 0b011001; // 0x19 = auto-heat
case 8: return 0b101001; // 0x29 = auto-heat+tank
case 9: return 0b011010; // 0x1A = auto-cool
case 10: return 0b101010; // 0x2A = auto-cool+tank
default: return 0; // do nothing
}
}
// start of optional pcb commands
//start of optional pcb commands
uint8_t PanasonicCommand::temp2hex(float temp) {
int hextemp = 0;
if (temp > 120) {
hextemp = 0;
} else if (temp < -78) {
hextemp = 255;
} else {
uint8_t Uref = 255;
int constant = 3695;
int R25 = 6340;
uint8_t T25 = 25;
int Rf = 6480;
float K = 273.15;
float RT = R25 * exp(constant * (1 / (temp + K) - 1 / (T25 + K)));
hextemp = Uref * (RT / (Rf + RT));
}
uint8_t PanasonicCommand::temp2hex(float temp)
{
int hextemp = 0;
if (temp > 120)
{
hextemp = 0;
}
else if (temp < -78)
{
hextemp = 255;
}
else
{
uint8_t Uref = 255;
int constant = 3695;
int R25 = 6340;
uint8_t T25 = 25;
int Rf = 6480;
float K = 273.15;
float RT = R25 * exp(constant * (1 / (temp + K) - 1 / (T25 + K)));
hextemp = Uref * (RT / (Rf + RT));
}
return hextemp;
}
return hextemp;
}
uint8_t PanasonicCommand::setByte6(uint8_t byte6, int val, int base, int bit) {
return (byte6 & ~(base << bit)) | (val << bit);
}
uint8_t PanasonicCommand::setByte6(uint8_t byte6, int val, int base, int bit)
{
return (byte6 & ~(base << bit)) | (val << bit);
}
uint8_t PanasonicCommand::setDemandControl(size_t input)
{
switch (input)
{
case 0: return 0xEB;
case 1: return 0xB8;
case 2: return 0x85;
case 3: return 0x52;
case 4: return 0x2B;
default: return 0; // do nothing
}
}
} // namespace panasonic_heatpump
} // namespace esphome
uint8_t PanasonicCommand::setDemandControl(size_t input) {
switch (input) {
case 0:
return 0xEB;
case 1:
return 0xB8;
case 2:
return 0x85;
case 3:
return 0x52;
case 4:
return 0x2B;
default:
return 0; // do nothing
}
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -15,35 +15,31 @@
#define OPTIONAL_MESSAGE_SIZE 20
#endif
namespace esphome {
namespace panasonic_heatpump {
class PanasonicCommand {
public:
PanasonicCommand() = delete;
namespace esphome
{
namespace panasonic_heatpump
{
class PanasonicCommand
{
public:
PanasonicCommand() = delete;
static uint8_t calcChecksum(std::vector<uint8_t>& data, int length);
static uint8_t setMultiply2(size_t input);
static uint8_t setMultiply4(size_t input);
static uint8_t setPlus1Multiply4(size_t input);
static uint8_t setPlus1Multiply8(size_t input);
static uint8_t setPlus1Multiply16(size_t input);
static uint8_t setPlus1Multiply64(size_t input);
static uint8_t setPlus1(size_t input);
static uint8_t setPlus128(size_t input);
static uint8_t setOperationMode(size_t input);
static uint8_t temp2hex(float temp);
static uint8_t setByte6(uint8_t byte6, int val, int base, int bit);
static uint8_t setDemandControl(size_t input);
static uint8_t calcChecksum(std::vector<uint8_t>& data, int length);
static uint8_t setMultiply2(size_t input);
static uint8_t setMultiply4(size_t input);
static uint8_t setPlus1Multiply4(size_t input);
static uint8_t setPlus1Multiply8(size_t input);
static uint8_t setPlus1Multiply16(size_t input);
static uint8_t setPlus1Multiply64(size_t input);
static uint8_t setPlus1(size_t input);
static uint8_t setPlus128(size_t input);
static uint8_t setOperationMode(size_t input);
static uint8_t temp2hex(float temp);
static uint8_t setByte6(uint8_t byte6, int val, int base, int bit);
static uint8_t setDemandControl(size_t input);
static const uint8_t InitialRequest[INIT_REQUEST_SIZE];
static const uint8_t InitialResponse[INIT_RESPONSE_SIZE];
static const uint8_t PollingMessage[DATA_MESSAGE_SIZE];
static const uint8_t PollingExtraMessage[DATA_MESSAGE_SIZE];
static const uint8_t CommandMessage[DATA_MESSAGE_SIZE];
};
} // namespace panasonic_heatpump
static const uint8_t InitialRequest[INIT_REQUEST_SIZE];
static const uint8_t InitialResponse[INIT_RESPONSE_SIZE];
static const uint8_t PollingMessage[DATA_MESSAGE_SIZE];
static const uint8_t PollingExtraMessage[DATA_MESSAGE_SIZE];
static const uint8_t CommandMessage[DATA_MESSAGE_SIZE];
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,278 +1,256 @@
#include "decode.h"
namespace esphome {
namespace panasonic_heatpump {
constexpr const char* const PanasonicDecode::DisabledEnabled[];
constexpr const char* const PanasonicDecode::BlockedFree[];
constexpr const char* const PanasonicDecode::OffOn[];
constexpr const char* const PanasonicDecode::InactiveActive[];
constexpr const char* const PanasonicDecode::PumpFlowRateMode[];
constexpr const char* const PanasonicDecode::HolidayState[];
constexpr const char* const PanasonicDecode::OperationMode[];
constexpr const char* const PanasonicDecode::PowerfulMode[];
constexpr const char* const PanasonicDecode::QuietMode[];
constexpr const char* const PanasonicDecode::ThreeWayValve[];
constexpr const char* const PanasonicDecode::TwoWayValve[];
constexpr const char* const PanasonicDecode::MixingValve[];
constexpr const char* const PanasonicDecode::ZoneState[];
constexpr const char* const PanasonicDecode::WaterTempControl[];
constexpr const char* const PanasonicDecode::SolarMode[];
constexpr const char* const PanasonicDecode::ZoneSensorType[];
constexpr const char* const PanasonicDecode::LiquidType[];
constexpr const char* const PanasonicDecode::ExtPadHeaterType[];
constexpr const char* const PanasonicDecode::BivalentMode[];
constexpr const char* const PanasonicDecode::ModelNames[];
namespace esphome
{
namespace panasonic_heatpump
{
constexpr const char* const PanasonicDecode::DisabledEnabled[];
constexpr const char* const PanasonicDecode::BlockedFree[];
constexpr const char* const PanasonicDecode::OffOn[];
constexpr const char* const PanasonicDecode::InactiveActive[];
constexpr const char* const PanasonicDecode::PumpFlowRateMode[];
constexpr const char* const PanasonicDecode::HolidayState[];
constexpr const char* const PanasonicDecode::OperationMode[];
constexpr const char* const PanasonicDecode::PowerfulMode[];
constexpr const char* const PanasonicDecode::QuietMode[];
constexpr const char* const PanasonicDecode::ThreeWayValve[];
constexpr const char* const PanasonicDecode::TwoWayValve[];
constexpr const char* const PanasonicDecode::MixingValve[];
constexpr const char* const PanasonicDecode::ZoneState[];
constexpr const char* const PanasonicDecode::WaterTempControl[];
constexpr const char* const PanasonicDecode::SolarMode[];
constexpr const char* const PanasonicDecode::ZoneSensorType[];
constexpr const char* const PanasonicDecode::LiquidType[];
constexpr const char* const PanasonicDecode::ExtPadHeaterType[];
constexpr const char* const PanasonicDecode::BivalentMode[];
constexpr const char* const PanasonicDecode::ModelNames[];
constexpr const uint8_t KnownModels[NUMBER_OF_MODELS][10] = {
{0xE2, 0xCF, 0x0B, 0x13, 0x33, 0x32, 0xD1, 0x0C, 0x16, 0x33}, // 0
{0xE2, 0xCF, 0x0B, 0x14, 0x33, 0x42, 0xD1, 0x0B, 0x17, 0x33}, // 1
{0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0B, 0x05, 0x11}, // 2
{0xE2, 0xCF, 0x0C, 0x88, 0x05, 0x12, 0xD0, 0x0B, 0x97, 0x05}, // 3
{0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0C, 0x94, 0x05}, // 4
{0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0C, 0x95, 0x05}, // 5
{0xE2, 0xCF, 0x0D, 0x87, 0x05, 0x12, 0xD0, 0x0C, 0x96, 0x05}, // 6
{0xE2, 0xCE, 0x0D, 0x71, 0x81, 0x72, 0xCE, 0x0C, 0x92, 0x81}, // 7
{0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66}, // 8
{0xC2, 0xD3, 0x0B, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65}, // 9
{0xE2, 0xCF, 0x0B, 0x15, 0x33, 0x42, 0xD1, 0x0B, 0x18, 0x33}, // 10
{0xE2, 0xCF, 0x0B, 0x41, 0x34, 0x82, 0xD1, 0x0B, 0x31, 0x35}, // 11
{0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x47, 0x55}, // 12
{0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0D, 0x95, 0x05}, // 13
{0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0C, 0x91, 0x05}, // 14
{0xE2, 0xCF, 0x0C, 0x55, 0x14, 0x12, 0xD0, 0x0B, 0x15, 0x08}, // 15
{0xE2, 0xCF, 0x0C, 0x43, 0x00, 0x12, 0xD0, 0x0B, 0x15, 0x08}, // 16
{0x62, 0xD2, 0x0B, 0x45, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55}, // 17
{0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0C, 0x46, 0x55}, // 18
{0xE2, 0xCF, 0x0C, 0x54, 0x14, 0x12, 0xD0, 0x0B, 0x14, 0x08}, // 19
{0xC2, 0xD3, 0x0B, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65}, // 20
{0xC2, 0xD3, 0x0B, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65}, // 21
{0x62, 0xD2, 0x0B, 0x41, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55}, // 22
{0x32, 0xD4, 0x0B, 0x87, 0x84, 0x73, 0x90, 0x0C, 0x84, 0x84}, // 23
{0x32, 0xD4, 0x0B, 0x88, 0x84, 0x73, 0x90, 0x0C, 0x85, 0x84}, // 24
{0xE2, 0xCF, 0x0B, 0x75, 0x09, 0x12, 0xD0, 0x0C, 0x06, 0x11}, // 25
{0x42, 0xD4, 0x0B, 0x83, 0x71, 0x42, 0xD2, 0x0C, 0x46, 0x55}, // 26
{0xC2, 0xD3, 0x0C, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65}, // 27
{0xC2, 0xD3, 0x0C, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65}, // 28
{0xE2, 0xCF, 0x0B, 0x83, 0x05, 0x12, 0xD0, 0x0D, 0x92, 0x05}, // 29
{0xE2, 0xCF, 0x0C, 0x78, 0x09, 0x12, 0xD0, 0x0B, 0x06, 0x11}, // 30
{0xC2, 0xD3, 0x0C, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65}, // 31
{0x32, 0xD4, 0x0B, 0x99, 0x77, 0x62, 0x90, 0x0B, 0x01, 0x78}, // 32
{0x42, 0xD4, 0x0B, 0x15, 0x76, 0x12, 0xD0, 0x0B, 0x10, 0x11}, // 33
{0xE2, 0xD5, 0x0C, 0x29, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98}, // 34
{0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0E, 0x94, 0x05}, // 35
{0xE2, 0xD5, 0x0D, 0x36, 0x99, 0x02, 0xD6, 0x0F, 0x67, 0x95}, // 36
{0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x66, 0x95}, // 37
{0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x29, 0x98}, // 38
{0xE2, 0xCF, 0x0C, 0x89, 0x05, 0x12, 0xD0, 0x0C, 0x98, 0x05}, // 39
{0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x67, 0x95}, // 40
{0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x96, 0x05}, // 41
{0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x95, 0x05}, // 42
{0x32, 0xD4, 0x0B, 0x89, 0x84, 0x73, 0x90, 0x0C, 0x86, 0x84}, // 43
{0x32, 0xD4, 0x0B, 0x00, 0x78, 0x62, 0x90, 0x0B, 0x02, 0x78}, // 44
{0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0D, 0x91, 0x05}, // 45
{0xE2, 0xD5, 0x0D, 0x99, 0x94, 0x02, 0xD6, 0x0D, 0x68, 0x95}, // 46
{0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x95, 0x05}, // 47
{0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98}, // 48
{0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0C, 0x05, 0x11}, // 49
{0xE2, 0xCF, 0x0B, 0x44, 0x34, 0x12, 0xD0, 0x0C, 0x34, 0x35}, // 50
{0xE2, 0xD5, 0x0C, 0x67, 0x00, 0x83, 0x92, 0x0C, 0x27, 0x98}, // 51
{0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x27, 0x98}, // 52
{0x42, 0xD4, 0x0B, 0x83, 0x71, 0x32, 0xD2, 0x0C, 0x44, 0x55}, // 53
{0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x94, 0x05}, // 54
};
constexpr const uint8_t KnownModels[NUMBER_OF_MODELS][10] =
{
{ 0xE2, 0xCF, 0x0B, 0x13, 0x33, 0x32, 0xD1, 0x0C, 0x16, 0x33 }, // 0
{ 0xE2, 0xCF, 0x0B, 0x14, 0x33, 0x42, 0xD1, 0x0B, 0x17, 0x33 }, // 1
{ 0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0B, 0x05, 0x11 }, // 2
{ 0xE2, 0xCF, 0x0C, 0x88, 0x05, 0x12, 0xD0, 0x0B, 0x97, 0x05 }, // 3
{ 0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0C, 0x94, 0x05 }, // 4
{ 0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0C, 0x95, 0x05 }, // 5
{ 0xE2, 0xCF, 0x0D, 0x87, 0x05, 0x12, 0xD0, 0x0C, 0x96, 0x05 }, // 6
{ 0xE2, 0xCE, 0x0D, 0x71, 0x81, 0x72, 0xCE, 0x0C, 0x92, 0x81 }, // 7
{ 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66 }, // 8
{ 0xC2, 0xD3, 0x0B, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65 }, // 9
{ 0xE2, 0xCF, 0x0B, 0x15, 0x33, 0x42, 0xD1, 0x0B, 0x18, 0x33 }, // 10
{ 0xE2, 0xCF, 0x0B, 0x41, 0x34, 0x82, 0xD1, 0x0B, 0x31, 0x35 }, // 11
{ 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x47, 0x55 }, // 12
{ 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0D, 0x95, 0x05 }, // 13
{ 0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0C, 0x91, 0x05 }, // 14
{ 0xE2, 0xCF, 0x0C, 0x55, 0x14, 0x12, 0xD0, 0x0B, 0x15, 0x08 }, // 15
{ 0xE2, 0xCF, 0x0C, 0x43, 0x00, 0x12, 0xD0, 0x0B, 0x15, 0x08 }, // 16
{ 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55 }, // 17
{ 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0C, 0x46, 0x55 }, // 18
{ 0xE2, 0xCF, 0x0C, 0x54, 0x14, 0x12, 0xD0, 0x0B, 0x14, 0x08 }, // 19
{ 0xC2, 0xD3, 0x0B, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65 }, // 20
{ 0xC2, 0xD3, 0x0B, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65 }, // 21
{ 0x62, 0xD2, 0x0B, 0x41, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55 }, // 22
{ 0x32, 0xD4, 0x0B, 0x87, 0x84, 0x73, 0x90, 0x0C, 0x84, 0x84 }, // 23
{ 0x32, 0xD4, 0x0B, 0x88, 0x84, 0x73, 0x90, 0x0C, 0x85, 0x84 }, // 24
{ 0xE2, 0xCF, 0x0B, 0x75, 0x09, 0x12, 0xD0, 0x0C, 0x06, 0x11 }, // 25
{ 0x42, 0xD4, 0x0B, 0x83, 0x71, 0x42, 0xD2, 0x0C, 0x46, 0x55 }, // 26
{ 0xC2, 0xD3, 0x0C, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65 }, // 27
{ 0xC2, 0xD3, 0x0C, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65 }, // 28
{ 0xE2, 0xCF, 0x0B, 0x83, 0x05, 0x12, 0xD0, 0x0D, 0x92, 0x05 }, // 29
{ 0xE2, 0xCF, 0x0C, 0x78, 0x09, 0x12, 0xD0, 0x0B, 0x06, 0x11 }, // 30
{ 0xC2, 0xD3, 0x0C, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65 }, // 31
{ 0x32, 0xD4, 0x0B, 0x99, 0x77, 0x62, 0x90, 0x0B, 0x01, 0x78 }, // 32
{ 0x42, 0xD4, 0x0B, 0x15, 0x76, 0x12, 0xD0, 0x0B, 0x10, 0x11 }, // 33
{ 0xE2, 0xD5, 0x0C, 0x29, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98 }, // 34
{ 0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0E, 0x94, 0x05 }, // 35
{ 0xE2, 0xD5, 0x0D, 0x36, 0x99, 0x02, 0xD6, 0x0F, 0x67, 0x95 }, // 36
{ 0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x66, 0x95 }, // 37
{ 0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x29, 0x98 }, // 38
{ 0xE2, 0xCF, 0x0C, 0x89, 0x05, 0x12, 0xD0, 0x0C, 0x98, 0x05 }, // 39
{ 0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x67, 0x95 }, // 40
{ 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x96, 0x05 }, // 41
{ 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x95, 0x05 }, // 42
{ 0x32, 0xD4, 0x0B, 0x89, 0x84, 0x73, 0x90, 0x0C, 0x86, 0x84 }, // 43
{ 0x32, 0xD4, 0x0B, 0x00, 0x78, 0x62, 0x90, 0x0B, 0x02, 0x78 }, // 44
{ 0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0D, 0x91, 0x05 }, // 45
{ 0xE2, 0xD5, 0x0D, 0x99, 0x94, 0x02, 0xD6, 0x0D, 0x68, 0x95 }, // 46
{ 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x95, 0x05 }, // 47
{ 0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98 }, // 48
{ 0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0C, 0x05, 0x11 }, // 49
{ 0xE2, 0xCF, 0x0B, 0x44, 0x34, 0x12, 0xD0, 0x0C, 0x34, 0x35 }, // 50
{ 0xE2, 0xD5, 0x0C, 0x67, 0x00, 0x83, 0x92, 0x0C, 0x27, 0x98 }, // 51
{ 0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x27, 0x98 }, // 52
{ 0x42, 0xD4, 0x0B, 0x83, 0x71, 0x32, 0xD2, 0x0C, 0x44, 0x55 }, // 53
{ 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x94, 0x05 }, // 54
};
int PanasonicDecode::getBit1(uint8_t input) {
return input >> 7;
}
int PanasonicDecode::getBit1(uint8_t input)
{
return input >> 7;
int PanasonicDecode::getBit1and2(uint8_t input) {
return (input >> 6) - 1;
}
int PanasonicDecode::getBit3and4(uint8_t input) {
return ((input >> 4) & 0b11) - 1;
}
int PanasonicDecode::getBit5and6(uint8_t input) {
return ((input >> 2) & 0b11) - 1;
}
int PanasonicDecode::getBit7and8(uint8_t input) {
return (input & 0b11) - 1;
}
int PanasonicDecode::getBit3and4and5(uint8_t input) {
return ((input >> 3) & 0b111) - 1;
}
int PanasonicDecode::getBit6and7and8(uint8_t input) {
return (input & 0b111) - 1;
}
int PanasonicDecode::getByteMinus1(uint8_t input) {
return (int)input - 1;
}
int PanasonicDecode::getByteMinus128(uint8_t input) {
return (int)input - 128;
}
float PanasonicDecode::getByteMinus1Div2(uint8_t input) {
return ((float)input - 1) / 2;
}
float PanasonicDecode::getByteMinus1Div5(uint8_t input) {
return ((float)input - 1) / 5;
}
float PanasonicDecode::getByteMinus1Div50(uint8_t input) {
return ((float)input - 1) / 50;
}
int PanasonicDecode::getByteMinus1Times10(uint8_t input) {
return ((int)input - 1) * 10;
}
int PanasonicDecode::getByteMinus1Times50(uint8_t input) {
return ((int)input - 1) * 50;
}
int PanasonicDecode::getByteMinus1Times200(uint8_t input) {
return ((int)input - 1) * 200;
}
int PanasonicDecode::getHighNibbleMinus1(uint8_t input) {
return (input >> 4) - 1;
}
int PanasonicDecode::getLowNibbleMinus1(uint8_t input) {
return (input & 0b1111) - 1;
}
int PanasonicDecode::getWordMinus1(const std::vector<uint8_t>& data, uint8_t index) {
return ((data[index + 1] << 8) + data[index]) - 1;
}
int PanasonicDecode::getUintt16(const std::vector<uint8_t>& data, uint8_t index) {
return (static_cast<uint16_t>((data[index + 1] << 8) | data[index])) - 1;
}
// TOP4 //
int PanasonicDecode::getOperationMode(uint8_t input) {
switch ((int)(input & 0b111111)) {
case 0b100001:
return 0; // 0x21 = tank
case 0b010010:
return 1; // 0x12 = heat
case 0b100010:
return 2; // 0x22 = heat+tank
case 0b010011:
return 3; // 0x13 = cool
case 0b100011:
return 4; // 0x23 = cool+tank
case 0b011000:
return 5; // 0x18 = auto
case 0b101000:
return 6; // 0x28 = auto-tank
case 0b011001:
return 7; // 0x19 = auto-heat
case 0b101001:
return 8; // 0x29 = auto-heat+tank
case 0b011010:
return 9; // 0x1A = auto-cool
case 0b101010:
return 10; // 0x2A = auto-cool+tank
default:
return -1; // unknown
}
}
// TOP92 //
int PanasonicDecode::getModel(const std::vector<uint8_t>& data, uint8_t index) {
// Ensure there are at least 10 bytes in data message after the start index
if (index + 10 > data.size())
return -1;
// Get model bytes from data message
auto model = std::vector<uint8_t>(data.begin() + index, data.begin() + index + 10);
// Search model bytes in the list of known models and return the index
for (size_t i = 0; i < NUMBER_OF_MODELS; i++) {
if (std::equal(KnownModels[i], KnownModels[i] + 10, model.begin())) {
return static_cast<int>(i);
}
}
int PanasonicDecode::getBit1and2(uint8_t input)
{
return (input >> 6) - 1;
}
return -1;
}
int PanasonicDecode::getBit3and4(uint8_t input)
{
return ((input >> 4) & 0b11) - 1;
}
// TOP1 //
// input1 = data[169]
// input2 = data[170]
float PanasonicDecode::getPumpFlow(const std::vector<uint8_t>& data, uint8_t index) {
return (((float)data[index] - 1) / 256) + ((int)data[index + 1]);
}
int PanasonicDecode::getBit5and6(uint8_t input)
{
return ((input >> 2) & 0b11) - 1;
}
// TOP5, TOP6 //
float PanasonicDecode::getFractional(uint8_t input, uint8_t shift) {
return ((int)((input >> shift) & 0b111) - 1) * 0.25;
}
int PanasonicDecode::getBit7and8(uint8_t input)
{
return (input & 0b11) - 1;
}
// TOP44 //
// errorType = data[113]
// errorNumber = data[114]
std::string PanasonicDecode::getErrorInfo(uint8_t errorType, uint8_t errorNumber) {
int error_type = (int)(errorType);
int error_number = ((int)(errorNumber)) - 17;
char error_string[10];
int PanasonicDecode::getBit3and4and5(uint8_t input)
{
return ((input >> 3) & 0b111) - 1;
}
switch (error_type) {
case 177: // B1=F type error
sprintf(error_string, "F%02X", error_number);
break;
case 161: // A1=H type error
sprintf(error_string, "H%02X", error_number);
break;
default:
sprintf(error_string, "No error");
break;
}
int PanasonicDecode::getBit6and7and8(uint8_t input)
{
return (input & 0b111) - 1;
}
return error_string;
}
int PanasonicDecode::getByteMinus1(uint8_t input)
{
return (int)input - 1;
}
bool PanasonicDecode::getBinaryState(uint8_t input) {
return input != 0;
}
int PanasonicDecode::getByteMinus128(uint8_t input)
{
return (int)input - 128;
}
std::string PanasonicDecode::getTextState(const char* const array[], int index) {
int size = atoi(array[0]);
float PanasonicDecode::getByteMinus1Div2(uint8_t input)
{
return ((float)input - 1) / 2;
}
if ((index < 0) || (index >= size)) {
return "UNDEFINED";
}
float PanasonicDecode::getByteMinus1Div5(uint8_t input)
{
return ((float)input - 1) / 5;
}
float PanasonicDecode::getByteMinus1Div50(uint8_t input)
{
return ((float)input - 1) / 50;
}
int PanasonicDecode::getByteMinus1Times10(uint8_t input)
{
return ((int)input - 1) * 10;
}
int PanasonicDecode::getByteMinus1Times50(uint8_t input)
{
return ((int)input - 1) * 50;
}
int PanasonicDecode::getByteMinus1Times200(uint8_t input)
{
return ((int)input - 1) * 200;
}
int PanasonicDecode::getHighNibbleMinus1(uint8_t input)
{
return (input >> 4) - 1;
}
int PanasonicDecode::getLowNibbleMinus1(uint8_t input)
{
return (input & 0b1111) - 1;
}
int PanasonicDecode::getWordMinus1(const std::vector<uint8_t>& data, uint8_t index)
{
return ((data[index + 1] << 8) + data[index]) - 1;
}
int PanasonicDecode::getUintt16(const std::vector<uint8_t>& data, uint8_t index)
{
return (static_cast<uint16_t>((data[index + 1] << 8) | data[index])) - 1;
}
// TOP4 //
int PanasonicDecode::getOperationMode(uint8_t input)
{
switch ((int)(input & 0b111111))
{
case 0b100001: return 0; // 0x21 = tank
case 0b010010: return 1; // 0x12 = heat
case 0b100010: return 2; // 0x22 = heat+tank
case 0b010011: return 3; // 0x13 = cool
case 0b100011: return 4; // 0x23 = cool+tank
case 0b011000: return 5; // 0x18 = auto
case 0b101000: return 6; // 0x28 = auto-tank
case 0b011001: return 7; // 0x19 = auto-heat
case 0b101001: return 8; // 0x29 = auto-heat+tank
case 0b011010: return 9; // 0x1A = auto-cool
case 0b101010: return 10; // 0x2A = auto-cool+tank
default: return -1; // unknown
}
}
// TOP92 //
int PanasonicDecode::getModel(const std::vector<uint8_t>& data, uint8_t index)
{
// Ensure there are at least 10 bytes in data message after the start index
if (index + 10 > data.size()) return -1;
// Get model bytes from data message
auto model = std::vector<uint8_t>(data.begin() + index, data.begin() + index + 10);
// Search model bytes in the list of known models and return the index
for (size_t i = 0; i < NUMBER_OF_MODELS; i++)
{
if (std::equal(KnownModels[i], KnownModels[i] + 10, model.begin()))
{
return static_cast<int>(i);
}
}
return -1;
}
// TOP1 //
// input1 = data[169]
// input2 = data[170]
float PanasonicDecode::getPumpFlow(const std::vector<uint8_t>& data, uint8_t index)
{
return (((float)data[index] - 1) / 256) + ((int)data[index + 1]);
}
// TOP5, TOP6 //
float PanasonicDecode::getFractional(uint8_t input, uint8_t shift)
{
return ((int)((input >> shift) & 0b111) - 1) * 0.25;
}
// TOP44 //
// errorType = data[113]
// errorNumber = data[114]
std::string PanasonicDecode::getErrorInfo(uint8_t errorType, uint8_t errorNumber)
{
int error_type = (int)(errorType);
int error_number = ((int)(errorNumber)) - 17;
char error_string[10];
switch (error_type)
{
case 177: // B1=F type error
sprintf(error_string, "F%02X", error_number);
break;
case 161: // A1=H type error
sprintf(error_string, "H%02X", error_number);
break;
default:
sprintf(error_string, "No error");
break;
}
return error_string;
}
bool PanasonicDecode::getBinaryState(uint8_t input)
{
return input != 0;
}
std::string PanasonicDecode::getTextState(const char* const array[], int index)
{
int size = atoi(array[0]);
if ((index < 0) || (index >= size))
{
return "UNDEFINED";
}
return array[index + 1];
}
} // namespace panasonic_heatpump
return array[index + 1];
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -10,129 +10,123 @@
#define NUMBER_OF_MODELS 55
#endif
namespace esphome {
namespace panasonic_heatpump {
class PanasonicDecode {
public:
PanasonicDecode() = delete;
namespace esphome
{
namespace panasonic_heatpump
{
class PanasonicDecode
{
public:
PanasonicDecode() = delete;
static int getBit1(uint8_t input);
static int getBit1and2(uint8_t input);
static int getBit3and4(uint8_t input);
static int getBit5and6(uint8_t input);
static int getBit7and8(uint8_t input);
static int getBit3and4and5(uint8_t input);
static int getBit6and7and8(uint8_t input);
static int getByteMinus1(uint8_t input);
static int getByteMinus128(uint8_t input);
static float getByteMinus1Div2(uint8_t input);
static float getByteMinus1Div5(uint8_t input);
static float getByteMinus1Div50(uint8_t input);
static int getByteMinus1Times10(uint8_t input);
static int getByteMinus1Times50(uint8_t input);
static int getByteMinus1Times200(uint8_t input);
static int getHighNibbleMinus1(uint8_t input);
static int getLowNibbleMinus1(uint8_t input);
static int getWordMinus1(const std::vector<uint8_t>& data, uint8_t index);
static int getUintt16(const std::vector<uint8_t>& data, uint8_t index);
static int getBit1(uint8_t input);
static int getBit1and2(uint8_t input);
static int getBit3and4(uint8_t input);
static int getBit5and6(uint8_t input);
static int getBit7and8(uint8_t input);
static int getBit3and4and5(uint8_t input);
static int getBit6and7and8(uint8_t input);
static int getByteMinus1(uint8_t input);
static int getByteMinus128(uint8_t input);
static float getByteMinus1Div2(uint8_t input);
static float getByteMinus1Div5(uint8_t input);
static float getByteMinus1Div50(uint8_t input);
static int getByteMinus1Times10(uint8_t input);
static int getByteMinus1Times50(uint8_t input);
static int getByteMinus1Times200(uint8_t input);
static int getHighNibbleMinus1(uint8_t input);
static int getLowNibbleMinus1(uint8_t input);
static int getWordMinus1(const std::vector<uint8_t>& data, uint8_t index);
static int getUintt16(const std::vector<uint8_t>& data, uint8_t index);
static int getOperationMode(uint8_t input);
static int getModel(const std::vector<uint8_t>& data, uint8_t index);
static float getPumpFlow(const std::vector<uint8_t>& data, uint8_t index);
static float getFractional(uint8_t input, uint8_t shift);
static std::string getErrorInfo(uint8_t errorType, uint8_t errorNumber);
static int getOperationMode(uint8_t input);
static int getModel(const std::vector<uint8_t>& data, uint8_t index);
static float getPumpFlow(const std::vector<uint8_t>& data, uint8_t index);
static float getFractional(uint8_t input, uint8_t shift);
static std::string getErrorInfo(uint8_t errorType, uint8_t errorNumber);
static bool getBinaryState(uint8_t input);
static std::string getTextState(const char* const array[], int index);
static bool getBinaryState(uint8_t input);
static std::string getTextState(const char* const array[], int index);
static const constexpr char* const DisabledEnabled[] = {"2", "Disabled", "Enabled"};
static const constexpr char* const BlockedFree[] = {"2", "Blocked", "Free"};
static const constexpr char* const OffOn[] = {"2", "Off", "On"};
static const constexpr char* const InactiveActive[] = {"2", "Inactive", "Active"};
static const constexpr char* const PumpFlowRateMode[] = {"2", "DeltaT", "Max flow"};
static const constexpr char* const ThreeWayValve[] = {"2", "Buffer Tank", "DHW Tank"};
static const constexpr char* const TwoWayValve[] = {"2", "Cool", "Heat"};
static const constexpr char* const WaterTempControl[] = {"2", "Comp. Curve", "Direct"};
static const constexpr char* const LiquidType[] = {"2", "Water", "Glycol"};
static const constexpr char* const HolidayState[] = {"3", "Off", "Scheduled", "Active"};
static const constexpr char* const SolarMode[] = {"3", "Disabled", "Buffer Tank", "DHW Tank"};
static const constexpr char* const MixingValve[] = {"4", "Off", "Decrease", "Increase", "Invalid"};
static const constexpr char* const ZoneSensorType[] = {"4", "Water Temperature", "External Thermostat",
"Internal Thermostat", "Thermistor"};
static const constexpr char* const QuietMode[] = {"4", "Off", "Level 1", "Level 2", "Level 3"};
static const constexpr char* const PowerfulMode[] = {"4", "Off", "30min", "60min", "90min"};
static const constexpr char* const OperationMode[] = {
"11", "TANK", "HEAT", "HEAT+TANK", "COOL", "COOL+TANK",
"AUTO", "AUTO+TANK", "AUTO(HEAT)", "AUTO(HEAT)+TANK", "AUTO(COOL)", "AUTO(COOL)+TANK"};
static const constexpr char* const ZoneState[] = {"3", "Zone 1", "Zone 2", "Zone 1 & 2"};
static const constexpr char* const ExtPadHeaterType[] = {"3", "Disabled", "Type-A", "Type-B"};
static const constexpr char* const BivalentMode[] = {"3", "Alternative", "Parallel", "Advanced Parallel"};
static const constexpr char* const ModelNames[] = {
"55", // string representation of number of known models (last model number + 1)
"WH-MDC05H3E5", // 0
"WH-MDC07H3E5", // 1
"IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", // 2
"IDU:WH-SDC09H3E8, ODU:WH-UD09HE8", // 3
"IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", // 4
"IDU:WH-SXC12H9E8, ODU:WH-UX12HE8", // 5
"IDU:WH-SXC16H9E8, ODU:WH-UX16HE8", // 6
"IDU:WH-SDC05H3E5, ODU:WH-UD05HE5", // 7
"IDU:WH-SDC0709J3E5, ODU:WH-UD09JE5", // 8
"WH-MDC05J3E5", // 9
"WH-MDC09H3E5", // 10
"WH-MXC09H3E5", // 11
"IDU:WH-ADC0309J3E5, ODU:WH-UD09JE5", // 12
"IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", // 13
"IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", // 14
"IDU:WH-SDC09H3E5, ODU:WH-UD09HE5", // 15
"IDU:WH-ADC0309H3E5, ODU:WH-UD09HE5", // 16
"IDU:WH-ADC0309J3E5, ODU:WH-UD05JE5", // 17
"IDU:WH-SDC0709J3E5, ODU:WH-UD07JE5", // 18
"IDU:WH-SDC07H3E5-1, ODU:WH-UD07HE5-1", // 19
"WH-MDC07J3E5", // 20
"WH-MDC09J3E5", // 21
"IDU:WH-SDC0305J3E5, ODU:WH-UD05JE5", // 22
"WH-MXC09J3E8", // 23
"WH-MXC12J9E8", // 24
"IDU:WH-ADC1216H6E5, ODU:WH-UD12HE5", // 25
"IDU:WH-ADC0309J3E5C, ODU:WH-UD07JE5", // 26
"WH-MDC07J3E5", // 27
"WH-MDC05J3E5", // 28
"IDU:WH-SQC12H9E8, ODU:WH-UQ12HE8", // 29
"IDU:WH-SXC12H6E5, ODU:WH-UX12HE5", // 30
"WH-MDC09J3E5", // 31
"WH-MXC09J3E5", // 32
"IDU:WH-ADC1216H6E5C, ODU:WH-UD12HE5", // 33
"IDU:WH-ADC0509L3E5, ODU:WH-WDG07LE5", // 34
"IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", // 35
"IDU:WH-ADC0309K3E5AN, ODU:WH-UDZ07KE5", // 36
"IDU:WH-SDC0309K3E5, ODU:WH-UDZ05KE5", // 37
"IDU:WH-SDC0509L3E5, ODU:WH-WDG09LE5", // 38
"IDU:WH-SDC12H9E8, ODU:WH-UD12HE8", // 39
"IDU:WH-SDC0309K3E5, ODU:WH-UDZ07KE5", // 40
"IDU:WH-ADC0916H9E8, ODU:WH-UX16HE8", // 41
"IDU:WH-ADC0912H9E8, ODU:WH-UX12HE8", // 42
"WH-MXC16J9E8", // 43
"WH-MXC12J6E5", // 44
"IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", // 45
"IDU:WH-ADC0309K3E5, ODU:WH-UDZ09KE5", // 46
"IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", // 47
"IDU:WH-SDC0509L3E5, ODU:WH-WDG07LE5", // 48
"IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", // 49
"WH-MXC12H9E8", // 50
"IDU:WH-ADC0509L3E5AN, ODU:WH-WDG05LE5", // 51
"IDU:WH-SDC0509L3E5, ODU:WH-WDG05LE5", // 52
"IDU:WH-ADC0309J3E5C, ODU:WH-UD03JE5", // 53
"IDU:WH-ADC0916H9E8, ODU:WH-UX09HE8", // 54
};
};
static const constexpr char* const DisabledEnabled[] = { "2", "Disabled", "Enabled" };
static const constexpr char* const BlockedFree[] = { "2", "Blocked", "Free" };
static const constexpr char* const OffOn[] = { "2", "Off", "On" };
static const constexpr char* const InactiveActive[] = { "2", "Inactive", "Active" };
static const constexpr char* const PumpFlowRateMode[] = { "2", "DeltaT", "Max flow" };
static const constexpr char* const ThreeWayValve[] = { "2", "Buffer Tank", "DHW Tank" };
static const constexpr char* const TwoWayValve[] = { "2", "Cool", "Heat" };
static const constexpr char* const WaterTempControl[] = { "2", "Comp. Curve", "Direct" };
static const constexpr char* const LiquidType[] = { "2", "Water", "Glycol" };
static const constexpr char* const HolidayState[] = { "3", "Off", "Scheduled", "Active" };
static const constexpr char* const SolarMode[] = { "3", "Disabled", "Buffer Tank", "DHW Tank" };
static const constexpr char* const MixingValve[] = { "4", "Off", "Decrease","Increase", "Invalid" };
static const constexpr char* const ZoneSensorType[] = { "4", "Water Temperature", "External Thermostat", "Internal Thermostat", "Thermistor" };
static const constexpr char* const QuietMode[] = { "4", "Off", "Level 1", "Level 2", "Level 3" };
static const constexpr char* const PowerfulMode[] = { "4", "Off", "30min", "60min", "90min" };
static const constexpr char* const OperationMode[] =
{
"11", "TANK", "HEAT", "HEAT+TANK", "COOL", "COOL+TANK", "AUTO", "AUTO+TANK",
"AUTO(HEAT)", "AUTO(HEAT)+TANK", "AUTO(COOL)", "AUTO(COOL)+TANK"
};
static const constexpr char* const ZoneState[] = { "3", "Zone 1", "Zone 2", "Zone 1 & 2" };
static const constexpr char* const ExtPadHeaterType[] = { "3", "Disabled", "Type-A", "Type-B" };
static const constexpr char* const BivalentMode[] = { "3", "Alternative", "Parallel", "Advanced Parallel" };
static const constexpr char* const ModelNames[] =
{
"55", // string representation of number of known models (last model number + 1)
"WH-MDC05H3E5", // 0
"WH-MDC07H3E5", // 1
"IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", // 2
"IDU:WH-SDC09H3E8, ODU:WH-UD09HE8", // 3
"IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", // 4
"IDU:WH-SXC12H9E8, ODU:WH-UX12HE8", // 5
"IDU:WH-SXC16H9E8, ODU:WH-UX16HE8", // 6
"IDU:WH-SDC05H3E5, ODU:WH-UD05HE5", // 7
"IDU:WH-SDC0709J3E5, ODU:WH-UD09JE5", // 8
"WH-MDC05J3E5", // 9
"WH-MDC09H3E5", // 10
"WH-MXC09H3E5", // 11
"IDU:WH-ADC0309J3E5, ODU:WH-UD09JE5", // 12
"IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", // 13
"IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", // 14
"IDU:WH-SDC09H3E5, ODU:WH-UD09HE5", // 15
"IDU:WH-ADC0309H3E5, ODU:WH-UD09HE5", // 16
"IDU:WH-ADC0309J3E5, ODU:WH-UD05JE5", // 17
"IDU:WH-SDC0709J3E5, ODU:WH-UD07JE5", // 18
"IDU:WH-SDC07H3E5-1, ODU:WH-UD07HE5-1", // 19
"WH-MDC07J3E5", // 20
"WH-MDC09J3E5", // 21
"IDU:WH-SDC0305J3E5, ODU:WH-UD05JE5", // 22
"WH-MXC09J3E8", // 23
"WH-MXC12J9E8", // 24
"IDU:WH-ADC1216H6E5, ODU:WH-UD12HE5", // 25
"IDU:WH-ADC0309J3E5C, ODU:WH-UD07JE5", // 26
"WH-MDC07J3E5", // 27
"WH-MDC05J3E5", // 28
"IDU:WH-SQC12H9E8, ODU:WH-UQ12HE8", // 29
"IDU:WH-SXC12H6E5, ODU:WH-UX12HE5", // 30
"WH-MDC09J3E5", // 31
"WH-MXC09J3E5", // 32
"IDU:WH-ADC1216H6E5C, ODU:WH-UD12HE5", // 33
"IDU:WH-ADC0509L3E5, ODU:WH-WDG07LE5", // 34
"IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", // 35
"IDU:WH-ADC0309K3E5AN, ODU:WH-UDZ07KE5", // 36
"IDU:WH-SDC0309K3E5, ODU:WH-UDZ05KE5", // 37
"IDU:WH-SDC0509L3E5, ODU:WH-WDG09LE5", // 38
"IDU:WH-SDC12H9E8, ODU:WH-UD12HE8", // 39
"IDU:WH-SDC0309K3E5, ODU:WH-UDZ07KE5", // 40
"IDU:WH-ADC0916H9E8, ODU:WH-UX16HE8", // 41
"IDU:WH-ADC0912H9E8, ODU:WH-UX12HE8", // 42
"WH-MXC16J9E8", // 43
"WH-MXC12J6E5", // 44
"IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", // 45
"IDU:WH-ADC0309K3E5, ODU:WH-UDZ09KE5", // 46
"IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", // 47
"IDU:WH-SDC0509L3E5, ODU:WH-WDG07LE5", // 48
"IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", // 49
"WH-MXC12H9E8", // 50
"IDU:WH-ADC0509L3E5AN, ODU:WH-WDG05LE5", // 51
"IDU:WH-SDC0509L3E5, ODU:WH-WDG05LE5", // 52
"IDU:WH-ADC0309J3E5C, ODU:WH-UD03JE5", // 53
"IDU:WH-ADC0916H9E8, ODU:WH-UX09HE8", // 54
};
};
extern const uint8_t KnownModels[NUMBER_OF_MODELS][10];
} // namespace panasonic_heatpump
extern const uint8_t KnownModels[NUMBER_OF_MODELS][10];
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,66 +1,59 @@
#include "helpers.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump";
void PanasonicHelpers::log_uart_hex(UartLogDirection direction, const std::vector<uint8_t>& data,
const char separator) {
PanasonicHelpers::log_uart_hex(direction, &data[0], data.size(), separator);
}
void PanasonicHelpers::log_uart_hex(UartLogDirection direction, const std::vector<uint8_t>& data, const char separator)
{
PanasonicHelpers::log_uart_hex(direction, &data[0], data.size(), separator);
}
void PanasonicHelpers::log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length,
const char separator) {
std::string logStr = "";
std::string msgDir = direction == UART_LOG_TX ? ">>>" : "<<<";
std::string msgType = direction == UART_LOG_TX ? "request" : "response";
switch (data[0]) {
case 0x31:
msgType = "initial_" + msgType;
break;
case 0x71:
msgType = "polling_" + msgType;
if (data[3] == 0x21)
msgType = "extra_" + msgType;
break;
case 0xF1:
msgType = "command_" + msgType;
break;
};
void PanasonicHelpers::log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length, const char separator)
{
std::string logStr = "";
std::string msgDir = direction == UART_LOG_TX ? ">>>" : "<<<";
std::string msgType = direction == UART_LOG_TX ? "request" : "response";
switch(data[0])
{
case 0x31:
msgType = "initial_" + msgType;
break;
case 0x71:
msgType = "polling_" + msgType;
if (data[3] == 0x21) msgType = "extra_" + msgType;
break;
case 0xF1:
msgType = "command_" + msgType;
break;
};
ESP_LOGI(TAG, "%s %s[%i]", msgDir.c_str(), msgType.c_str(), length);
delay(10);
ESP_LOGI(TAG, "%s %s[%i]", msgDir.c_str(), msgType.c_str(), length);
delay(10);
logStr += byte_array_to_hex_string(data, length, separator);
logStr += byte_array_to_hex_string(data, length, separator);
for (size_t i = 0; i < logStr.length(); i += UART_LOG_CHUNK_SIZE) {
ESP_LOGI(TAG, "%s %s", msgDir.c_str(), logStr.substr(i, UART_LOG_CHUNK_SIZE).c_str());
delay(10);
}
}
for (size_t i = 0; i < logStr.length(); i += UART_LOG_CHUNK_SIZE)
{
ESP_LOGI(TAG, "%s %s", msgDir.c_str(), logStr.substr(i, UART_LOG_CHUNK_SIZE).c_str());
delay(10);
}
}
std::string PanasonicHelpers::byte_array_to_hex_string(const std::vector<uint8_t>& data, const char separator) {
return PanasonicHelpers::byte_array_to_hex_string(&data[0], data.size(), separator);
}
std::string PanasonicHelpers::byte_array_to_hex_string(const std::vector<uint8_t>& data, const char separator)
{
return PanasonicHelpers::byte_array_to_hex_string(&data[0], data.size(), separator);
}
std::string PanasonicHelpers::byte_array_to_hex_string(const uint8_t* data, const size_t length, const char separator) {
std::string hexStr = "";
char buffer[5];
std::string PanasonicHelpers::byte_array_to_hex_string(const uint8_t* data, const size_t length, const char separator)
{
std::string hexStr = "";
char buffer[5];
for (size_t i = 0; i < length; i++)
{
if (i > 0) hexStr += separator;
sprintf(buffer, "%02X", data[i]);
hexStr += buffer;
}
return hexStr;
}
} // namespace panasonic_heatpump
for (size_t i = 0; i < length; i++) {
if (i > 0)
hexStr += separator;
sprintf(buffer, "%02X", data[i]);
hexStr += buffer;
}
return hexStr;
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -9,24 +9,19 @@
#define UART_LOG_CHUNK_SIZE 120
#endif
namespace esphome {
namespace panasonic_heatpump {
enum UartLogDirection : uint8_t {
UART_LOG_RX,
UART_LOG_TX,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum UartLogDirection : uint8_t
{
UART_LOG_RX,
UART_LOG_TX,
};
class PanasonicHelpers
{
public:
static void log_uart_hex(UartLogDirection direction, const std::vector<uint8_t>& data, const char separator);
static void log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length, const char separator);
static std::string byte_array_to_hex_string(const std::vector<uint8_t>& data, const char separator);
static std::string byte_array_to_hex_string(const uint8_t* data, const size_t length, const char separator);
};
}
}
class PanasonicHelpers {
public:
static void log_uart_hex(UartLogDirection direction, const std::vector<uint8_t>& data, const char separator);
static void log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length, const char separator);
static std::string byte_array_to_hex_string(const std::vector<uint8_t>& data, const char separator);
static std::string byte_array_to_hex_string(const uint8_t* data, const size_t length, const char separator);
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -2,16 +2,19 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import number
from esphome.const import (
UNIT_CELSIUS,
UNIT_KELVIN,
UNIT_MINUTE,
ENTITY_CATEGORY_CONFIG,
CONF_MIN_VALUE,
CONF_MAX_VALUE,
CONF_STEP,
UNIT_CELSIUS,
UNIT_KELVIN,
UNIT_MINUTE,
ENTITY_CATEGORY_CONFIG,
CONF_MIN_VALUE,
CONF_MAX_VALUE,
CONF_STEP,
)
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
CONF_SET5 = "set5" # Set Z1 Heat Request Temperature
CONF_SET6 = "set6" # Set Z1 Cool Request Temperature
@ -48,284 +51,224 @@ CONF_SET37 = "set37" # Set Bivalent AP Start Temp
CONF_SET38 = "set38" # Set Bivalent AP Stop Temp
TYPES = [
CONF_SET5,
CONF_SET6,
CONF_SET7,
CONF_SET8,
CONF_SET11,
CONF_SET15,
CONF_SET16_01,
CONF_SET16_02,
CONF_SET16_03,
CONF_SET16_04,
CONF_SET16_05,
CONF_SET16_06,
CONF_SET16_07,
CONF_SET16_08,
CONF_SET16_09,
CONF_SET16_10,
CONF_SET16_11,
CONF_SET16_12,
CONF_SET16_13,
CONF_SET16_14,
CONF_SET16_15,
CONF_SET16_16,
CONF_SET18,
CONF_SET19,
CONF_SET20,
CONF_SET21,
CONF_SET22,
CONF_SET23,
CONF_SET27,
CONF_SET29,
CONF_SET36,
CONF_SET37,
CONF_SET38,
CONF_SET5,
CONF_SET6,
CONF_SET7,
CONF_SET8,
CONF_SET11,
CONF_SET15,
CONF_SET16_01,
CONF_SET16_02,
CONF_SET16_03,
CONF_SET16_04,
CONF_SET16_05,
CONF_SET16_06,
CONF_SET16_07,
CONF_SET16_08,
CONF_SET16_09,
CONF_SET16_10,
CONF_SET16_11,
CONF_SET16_12,
CONF_SET16_13,
CONF_SET16_14,
CONF_SET16_15,
CONF_SET16_16,
CONF_SET18,
CONF_SET19,
CONF_SET20,
CONF_SET21,
CONF_SET22,
CONF_SET23,
CONF_SET27,
CONF_SET29,
CONF_SET36,
CONF_SET37,
CONF_SET38,
]
def number_options(min_val, max_val, step) -> cv.Schema:
schema = cv.Schema({
cv.Optional(CONF_MIN_VALUE, default=min_val): cv.float_,
cv.Optional(CONF_MAX_VALUE, default=max_val): cv.float_,
cv.Optional(CONF_STEP, default=step): cv.float_range(min=1.0, max=10.0),
})
return schema
PanasonicHeatpumpNumber = panasonic_heatpump_ns.class_("PanasonicHeatpumpNumber", number.Number, cg.Component)
def number_options(min_val, max_val, step) -> cv.Schema:
schema = cv.Schema(
{
cv.Optional(CONF_MIN_VALUE, default=min_val): cv.float_,
cv.Optional(CONF_MAX_VALUE, default=max_val): cv.float_,
cv.Optional(CONF_STEP, default=step): cv.float_range(min=1.0, max=10.0),
}
)
return schema
PanasonicHeatpumpNumber = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpNumber", number.Number, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_SET5): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-5.0, 5.0, 1.0)
),
cv.Optional(CONF_SET6): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-5.0, 5.0, 1.0)
),
cv.Optional(CONF_SET7): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-5.0, 5.0, 1.0)
),
cv.Optional(CONF_SET8): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-5.0, 5.0, 1.0)
),
cv.Optional(CONF_SET11): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(40.0, 75.0, 1.0)
),
cv.Optional(CONF_SET15): number.number_schema(
PanasonicHeatpumpNumber,
).extend(
number_options(64.0, 254.0, 1.0)
),
cv.Optional(CONF_SET16_01): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(20.0, 60.0, 1.0)
),
cv.Optional(CONF_SET16_02): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(20.0, 60.0, 1.0)
),
cv.Optional(CONF_SET16_03): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(-20.0, 15.0, 1.0)
),
cv.Optional(CONF_SET16_04): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(-20.0, 15.0, 1.0)
),
cv.Optional(CONF_SET16_05): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(20.0, 60.0, 1.0)
),
cv.Optional(CONF_SET16_06): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(20.0, 60.0, 1.0)
),
cv.Optional(CONF_SET16_07): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(-20.0, 15.0, 1.0)
),
cv.Optional(CONF_SET16_08): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(-20.0, 15.0, 1.0)
),
cv.Optional(CONF_SET16_09): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(5.0, 20.0, 1.0)
),
cv.Optional(CONF_SET16_10): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(5.0, 20.0, 1.0)
),
cv.Optional(CONF_SET16_11): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(15.0, 30.0, 1.0)
),
cv.Optional(CONF_SET16_12): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(15.0, 30.0, 1.0)
),
cv.Optional(CONF_SET16_13): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(5.0, 20.0, 1.0)
),
cv.Optional(CONF_SET16_14): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(5.0, 20.0, 1.0)
),
cv.Optional(CONF_SET16_15): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(15.0, 30.0, 1.0)
),
cv.Optional(CONF_SET16_16): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(
number_options(15.0, 30.0, 1.0)
),
cv.Optional(CONF_SET18): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(1.0, 15.0, 1.0)
),
cv.Optional(CONF_SET19): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(1.0, 15.0, 1.0)
),
cv.Optional(CONF_SET20): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(112.0, -2.0, 1.0)
),
cv.Optional(CONF_SET21): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_MINUTE,
).extend(
number_options(0.0, 250.0, 1.0)
),
cv.Optional(CONF_SET22): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(1.0, 15.0, 1.0)
),
cv.Optional(CONF_SET23): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(1.0, 15.0, 1.0)
),
cv.Optional(CONF_SET27): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(
number_options(0.0, 10.0, 1.0)
),
cv.Optional(CONF_SET29): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(5.0, 35.0, 1.0)
),
cv.Optional(CONF_SET36): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-15.0, 35.0, 1.0)
),
cv.Optional(CONF_SET37): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-10.0, 0.0, 1.0)
),
cv.Optional(CONF_SET38): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(
number_options(-10.0, 0.0, 1.0)
),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_SET5): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-5.0, 5.0, 1.0)),
cv.Optional(CONF_SET6): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-5.0, 5.0, 1.0)),
cv.Optional(CONF_SET7): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-5.0, 5.0, 1.0)),
cv.Optional(CONF_SET8): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-5.0, 5.0, 1.0)),
cv.Optional(CONF_SET11): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(40.0, 75.0, 1.0)),
cv.Optional(CONF_SET15): number.number_schema(
PanasonicHeatpumpNumber,
).extend(number_options(64.0, 254.0, 1.0)),
cv.Optional(CONF_SET16_01): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(20.0, 60.0, 1.0)),
cv.Optional(CONF_SET16_02): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(20.0, 60.0, 1.0)),
cv.Optional(CONF_SET16_03): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(-20.0, 15.0, 1.0)),
cv.Optional(CONF_SET16_04): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(-20.0, 15.0, 1.0)),
cv.Optional(CONF_SET16_05): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(20.0, 60.0, 1.0)),
cv.Optional(CONF_SET16_06): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(20.0, 60.0, 1.0)),
cv.Optional(CONF_SET16_07): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(-20.0, 15.0, 1.0)),
cv.Optional(CONF_SET16_08): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(-20.0, 15.0, 1.0)),
cv.Optional(CONF_SET16_09): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(5.0, 20.0, 1.0)),
cv.Optional(CONF_SET16_10): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(5.0, 20.0, 1.0)),
cv.Optional(CONF_SET16_11): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(15.0, 30.0, 1.0)),
cv.Optional(CONF_SET16_12): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(15.0, 30.0, 1.0)),
cv.Optional(CONF_SET16_13): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(5.0, 20.0, 1.0)),
cv.Optional(CONF_SET16_14): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(5.0, 20.0, 1.0)),
cv.Optional(CONF_SET16_15): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(15.0, 30.0, 1.0)),
cv.Optional(CONF_SET16_16): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
entity_category=ENTITY_CATEGORY_CONFIG,
).extend(number_options(15.0, 30.0, 1.0)),
cv.Optional(CONF_SET18): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(1.0, 15.0, 1.0)),
cv.Optional(CONF_SET19): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(1.0, 15.0, 1.0)),
cv.Optional(CONF_SET20): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(112.0, -2.0, 1.0)),
cv.Optional(CONF_SET21): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_MINUTE,
).extend(number_options(0.0, 250.0, 1.0)),
cv.Optional(CONF_SET22): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(1.0, 15.0, 1.0)),
cv.Optional(CONF_SET23): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(1.0, 15.0, 1.0)),
cv.Optional(CONF_SET27): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_KELVIN,
).extend(number_options(0.0, 10.0, 1.0)),
cv.Optional(CONF_SET29): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(5.0, 35.0, 1.0)),
cv.Optional(CONF_SET36): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-15.0, 35.0, 1.0)),
cv.Optional(CONF_SET37): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-10.0, 0.0, 1.0)),
cv.Optional(CONF_SET38): number.number_schema(
PanasonicHeatpumpNumber,
unit_of_measurement=UNIT_CELSIUS,
).extend(number_options(-10.0, 0.0, 1.0)),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await number.new_number(
child_config,
min_value=child_config[CONF_MIN_VALUE],
max_value=child_config[CONF_MAX_VALUE],
step=child_config[CONF_STEP],
)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_number(var))
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await number.new_number(
child_config,
min_value=child_config[CONF_MIN_VALUE],
max_value=child_config[CONF_MAX_VALUE],
step=child_config[CONF_STEP],
)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_number(var))

View File

@ -1,276 +1,303 @@
#include "panasonic_heatpump_number.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.number";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.number";
void PanasonicHeatpumpNumber::dump_config() {
LOG_NUMBER("", "Panasonic Heatpump Number", this);
delay(10);
}
void PanasonicHeatpumpNumber::dump_config()
{
LOG_NUMBER("", "Panasonic Heatpump Number", this);
delay(10);
}
void PanasonicHeatpumpNumber::control(float value) {
switch (this->id_) {
case NumberIds::CONF_SET5:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 38);
break;
case NumberIds::CONF_SET6:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 39);
break;
case NumberIds::CONF_SET7:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 40);
break;
case NumberIds::CONF_SET8:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 41);
break;
case NumberIds::CONF_SET11:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 42);
break;
case NumberIds::CONF_SET15:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 45);
break;
case NumberIds::CONF_SET16_01:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 75);
break;
case NumberIds::CONF_SET16_02:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 76);
break;
case NumberIds::CONF_SET16_03:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 77);
break;
case NumberIds::CONF_SET16_04:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 78);
break;
case NumberIds::CONF_SET16_05:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 79);
break;
case NumberIds::CONF_SET16_06:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 80);
break;
case NumberIds::CONF_SET16_07:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 81);
break;
case NumberIds::CONF_SET16_08:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 82);
break;
case NumberIds::CONF_SET16_09:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 86);
break;
case NumberIds::CONF_SET16_10:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 87);
break;
case NumberIds::CONF_SET16_11:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 88);
break;
case NumberIds::CONF_SET16_12:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 89);
break;
case NumberIds::CONF_SET16_13:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 90);
break;
case NumberIds::CONF_SET16_14:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 91);
break;
case NumberIds::CONF_SET16_15:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 92);
break;
case NumberIds::CONF_SET16_16:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 93);
break;
case NumberIds::CONF_SET18:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 84);
break;
case NumberIds::CONF_SET19:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 94);
break;
case NumberIds::CONF_SET20:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 99);
break;
case NumberIds::CONF_SET21:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 104);
break;
case NumberIds::CONF_SET22:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 105);
break;
case NumberIds::CONF_SET23:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 106);
break;
case NumberIds::CONF_SET27:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 59);
break;
case NumberIds::CONF_SET29:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 83);
break;
case NumberIds::CONF_SET36:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 65);
break;
case NumberIds::CONF_SET37:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 66);
break;
case NumberIds::CONF_SET38:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 68);
break;
default:
return;
};
void PanasonicHeatpumpNumber::control(float value)
{
switch (this->id_)
{
case NumberIds::CONF_SET5:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 38);
break;
case NumberIds::CONF_SET6:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 39);
break;
case NumberIds::CONF_SET7:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 40);
break;
case NumberIds::CONF_SET8:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 41);
break;
case NumberIds::CONF_SET11:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 42);
break;
case NumberIds::CONF_SET15:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 45);
break;
case NumberIds::CONF_SET16_01:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 75);
break;
case NumberIds::CONF_SET16_02:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 76);
break;
case NumberIds::CONF_SET16_03:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 77);
break;
case NumberIds::CONF_SET16_04:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 78);
break;
case NumberIds::CONF_SET16_05:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 79);
break;
case NumberIds::CONF_SET16_06:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 80);
break;
case NumberIds::CONF_SET16_07:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 81);
break;
case NumberIds::CONF_SET16_08:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 82);
break;
case NumberIds::CONF_SET16_09:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 86);
break;
case NumberIds::CONF_SET16_10:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 87);
break;
case NumberIds::CONF_SET16_11:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 88);
break;
case NumberIds::CONF_SET16_12:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 89);
break;
case NumberIds::CONF_SET16_13:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 90);
break;
case NumberIds::CONF_SET16_14:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 91);
break;
case NumberIds::CONF_SET16_15:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 92);
break;
case NumberIds::CONF_SET16_16:
this->parent_->set_command_curve(PanasonicCommand::setPlus128(value), 93);
break;
case NumberIds::CONF_SET18:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 84);
break;
case NumberIds::CONF_SET19:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 94);
break;
case NumberIds::CONF_SET20:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 99);
break;
case NumberIds::CONF_SET21:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 104);
break;
case NumberIds::CONF_SET22:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 105);
break;
case NumberIds::CONF_SET23:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 106);
break;
case NumberIds::CONF_SET27:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 59);
break;
case NumberIds::CONF_SET29:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 83);
break;
case NumberIds::CONF_SET36:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 65);
break;
case NumberIds::CONF_SET37:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 66);
break;
case NumberIds::CONF_SET38:
this->parent_->set_command_byte(PanasonicCommand::setPlus128(value), 68);
break;
default: return;
};
this->publish_state(state);
this->keep_state_ = 2;
}
this->publish_state(state);
this->keep_state_ = 2;
}
void PanasonicHeatpumpNumber::publish_new_state(const std::vector<uint8_t>& data) {
if (this->keep_state_ > 0) {
this->keep_state_--;
return;
}
if (data.empty())
return;
void PanasonicHeatpumpNumber::publish_new_state(const std::vector<uint8_t>& data)
{
if (this->keep_state_ > 0)
{
this->keep_state_--;
return;
}
if (data.empty()) return;
float new_state;
switch (this->id_) {
case NumberIds::CONF_SET11:
new_state = PanasonicDecode::getByteMinus128(data[42]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET20:
new_state = PanasonicDecode::getByteMinus128(data[99]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET18:
new_state = PanasonicDecode::getByteMinus128(data[84]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET19:
new_state = PanasonicDecode::getByteMinus128(data[94]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET5:
new_state = PanasonicDecode::getByteMinus128(data[38]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET6:
new_state = PanasonicDecode::getByteMinus128(data[39]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_01:
new_state = PanasonicDecode::getByteMinus128(data[75]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_02:
new_state = PanasonicDecode::getByteMinus128(data[76]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_04:
new_state = PanasonicDecode::getByteMinus128(data[78]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_03:
new_state = PanasonicDecode::getByteMinus128(data[77]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET7:
new_state = PanasonicDecode::getByteMinus128(data[40]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET8:
new_state = PanasonicDecode::getByteMinus128(data[41]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_09:
new_state = PanasonicDecode::getByteMinus128(data[86]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_10:
new_state = PanasonicDecode::getByteMinus128(data[87]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_12:
new_state = PanasonicDecode::getByteMinus128(data[89]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_11:
new_state = PanasonicDecode::getByteMinus128(data[88]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET29:
new_state = PanasonicDecode::getByteMinus128(data[83]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_05:
new_state = PanasonicDecode::getByteMinus128(data[79]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_06:
new_state = PanasonicDecode::getByteMinus128(data[80]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_08:
new_state = PanasonicDecode::getByteMinus128(data[82]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_07:
new_state = PanasonicDecode::getByteMinus128(data[81]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_13:
new_state = PanasonicDecode::getByteMinus128(data[90]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_14:
new_state = PanasonicDecode::getByteMinus128(data[91]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_16:
new_state = PanasonicDecode::getByteMinus128(data[93]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET16_15:
new_state = PanasonicDecode::getByteMinus128(data[92]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET15:
new_state = PanasonicDecode::getByteMinus1(data[45]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET21:
new_state = PanasonicDecode::getByteMinus1(data[104]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET22:
new_state = PanasonicDecode::getByteMinus128(data[105]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET23:
new_state = PanasonicDecode::getByteMinus128(data[106]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET27:
new_state = PanasonicDecode::getByteMinus128(data[59]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET36:
new_state = PanasonicDecode::getByteMinus128(data[65]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET37:
new_state = PanasonicDecode::getByteMinus128(data[66]);
if (this->has_state() && this->state == new_state)
return;
break;
case NumberIds::CONF_SET38:
new_state = PanasonicDecode::getByteMinus128(data[68]);
if (this->has_state() && this->state == new_state)
return;
break;
default:
return;
};
float new_state;
switch (this->id_)
{
case NumberIds::CONF_SET11:
new_state = PanasonicDecode::getByteMinus128(data[42]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET20:
new_state = PanasonicDecode::getByteMinus128(data[99]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET18:
new_state = PanasonicDecode::getByteMinus128(data[84]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET19:
new_state = PanasonicDecode::getByteMinus128(data[94]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET5:
new_state = PanasonicDecode::getByteMinus128(data[38]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET6:
new_state = PanasonicDecode::getByteMinus128(data[39]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_01:
new_state = PanasonicDecode::getByteMinus128(data[75]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_02:
new_state = PanasonicDecode::getByteMinus128(data[76]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_04:
new_state = PanasonicDecode::getByteMinus128(data[78]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_03:
new_state = PanasonicDecode::getByteMinus128(data[77]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET7:
new_state = PanasonicDecode::getByteMinus128(data[40]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET8:
new_state = PanasonicDecode::getByteMinus128(data[41]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_09:
new_state = PanasonicDecode::getByteMinus128(data[86]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_10:
new_state = PanasonicDecode::getByteMinus128(data[87]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_12:
new_state = PanasonicDecode::getByteMinus128(data[89]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_11:
new_state = PanasonicDecode::getByteMinus128(data[88]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET29:
new_state = PanasonicDecode::getByteMinus128(data[83]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_05:
new_state = PanasonicDecode::getByteMinus128(data[79]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_06:
new_state = PanasonicDecode::getByteMinus128(data[80]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_08:
new_state = PanasonicDecode::getByteMinus128(data[82]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_07:
new_state = PanasonicDecode::getByteMinus128(data[81]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_13:
new_state = PanasonicDecode::getByteMinus128(data[90]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_14:
new_state = PanasonicDecode::getByteMinus128(data[91]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_16:
new_state = PanasonicDecode::getByteMinus128(data[93]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET16_15:
new_state = PanasonicDecode::getByteMinus128(data[92]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET15:
new_state = PanasonicDecode::getByteMinus1(data[45]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET21:
new_state = PanasonicDecode::getByteMinus1(data[104]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET22:
new_state = PanasonicDecode::getByteMinus128(data[105]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET23:
new_state = PanasonicDecode::getByteMinus128(data[106]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET27:
new_state = PanasonicDecode::getByteMinus128(data[59]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET36:
new_state = PanasonicDecode::getByteMinus128(data[65]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET37:
new_state = PanasonicDecode::getByteMinus128(data[66]);
if (this->has_state() && this->state == new_state) return;
break;
case NumberIds::CONF_SET38:
new_state = PanasonicDecode::getByteMinus128(data[68]);
if (this->has_state() && this->state == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -5,58 +5,55 @@
#include "../decode.h"
#include "../commands.h"
namespace esphome {
namespace panasonic_heatpump {
enum NumberIds : uint8_t {
CONF_SET5,
CONF_SET6,
CONF_SET7,
CONF_SET8,
CONF_SET11,
CONF_SET15,
CONF_SET16_01,
CONF_SET16_02,
CONF_SET16_03,
CONF_SET16_04,
CONF_SET16_05,
CONF_SET16_06,
CONF_SET16_07,
CONF_SET16_08,
CONF_SET16_09,
CONF_SET16_10,
CONF_SET16_11,
CONF_SET16_12,
CONF_SET16_13,
CONF_SET16_14,
CONF_SET16_15,
CONF_SET16_16,
CONF_SET18,
CONF_SET19,
CONF_SET20,
CONF_SET21,
CONF_SET22,
CONF_SET23,
CONF_SET27,
CONF_SET29,
CONF_SET36,
CONF_SET37,
CONF_SET38,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum NumberIds : uint8_t
{
CONF_SET5,
CONF_SET6,
CONF_SET7,
CONF_SET8,
CONF_SET11,
CONF_SET15,
CONF_SET16_01,
CONF_SET16_02,
CONF_SET16_03,
CONF_SET16_04,
CONF_SET16_05,
CONF_SET16_06,
CONF_SET16_07,
CONF_SET16_08,
CONF_SET16_09,
CONF_SET16_10,
CONF_SET16_11,
CONF_SET16_12,
CONF_SET16_13,
CONF_SET16_14,
CONF_SET16_15,
CONF_SET16_16,
CONF_SET18,
CONF_SET19,
CONF_SET20,
CONF_SET21,
CONF_SET22,
CONF_SET23,
CONF_SET27,
CONF_SET29,
CONF_SET36,
CONF_SET37,
CONF_SET38,
};
class PanasonicHeatpumpNumber : public number::Number,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpNumber() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
class PanasonicHeatpumpNumber : public number::Number, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpNumber() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
protected:
void control(float value) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
protected:
void control(float value) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,437 +1,400 @@
#include "panasonic_heatpump.h"
#include "esphome/core/application.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump";
void PanasonicHeatpumpComponent::dump_config() {
ESP_LOGW(TAG, "*** Panasonic Heatpump Component v%s ***", PANASONIC_HEATPUMP_VERSION);
delay(10); // NOLINT
}
void PanasonicHeatpumpComponent::dump_config()
{
ESP_LOGW(TAG, "*** Panasonic Heatpump Component v%s ***", PanasonicHeatpumpComponent::version.c_str());
void PanasonicHeatpumpComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Panasonic Heatpump ...");
delay(10); // NOLINT
this->check_uart_settings(9600, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
this->update();
}
void PanasonicHeatpumpComponent::update() {
if (this->uart_client_ != nullptr)
return;
this->next_request_ = this->send_extra_request_ ? RequestType::POLLING_EXTRA : RequestType::POLLING;
}
void PanasonicHeatpumpComponent::loop() {
switch (this->loop_state_) {
case LoopState::READ_RESPONSE:
this->read_response();
this->loop_state_ = LoopState::CHECK_RESPONSE;
break;
case LoopState::CHECK_RESPONSE:
this->current_response_ = this->check_response(this->response_message_);
switch (this->current_response_) {
case ResponseType::UNKNOWN:
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case ResponseType::STANDARD:
this->loop_state_ = LoopState::PUBLISH_SENSOR;
break;
case ResponseType::EXTRA:
this->loop_state_ = LoopState::PUBLISH_EXTRA_SENSOR;
break;
};
break;
case LoopState::PUBLISH_SENSOR:
for (auto* entity : this->sensors_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_BINARY_SENSOR;
break;
case LoopState::PUBLISH_BINARY_SENSOR:
for (auto* entity : this->binary_sensors_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_TEXT_SENSOR;
break;
case LoopState::PUBLISH_TEXT_SENSOR:
for (auto* entity : this->text_sensors_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_NUMBER;
break;
case LoopState::PUBLISH_NUMBER:
for (auto* entity : this->numbers_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_SELECT;
break;
case LoopState::PUBLISH_SELECT:
for (auto* entity : this->selects_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_SWITCH;
break;
case LoopState::PUBLISH_SWITCH:
for (auto* entity : this->switches_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_CLIMATE;
break;
case LoopState::PUBLISH_CLIMATE:
for (auto* entity : this->climates_) {
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case LoopState::PUBLISH_EXTRA_SENSOR:
for (auto* entity : this->extra_sensors_) {
entity->publish_new_state(this->heatpump_extra_message_);
}
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case LoopState::SEND_REQUEST:
this->send_request(this->next_request_);
this->loop_state_ = LoopState::READ_REQUEST;
break;
case LoopState::READ_REQUEST:
this->read_request();
this->loop_state_ = LoopState::RESTART_LOOP;
break;
default:
this->loop_state_ = LoopState::READ_RESPONSE;
break;
};
}
void PanasonicHeatpumpComponent::read_response() {
while (this->available()) {
// Read each byte from heatpump and forward it directly to the client (CZ-TAW1)
this->read_byte(&byte_);
if (this->uart_client_ != nullptr) {
this->uart_client_->write_byte(byte_);
}
// Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte
if (!this->response_receiving_) {
if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1)
continue;
this->response_message_.clear();
this->response_receiving_ = true;
}
// Add current byte to message buffer
this->response_message_.push_back(byte_);
// 2. byte contains the payload size
if (this->response_message_.size() == 2) {
this->payload_length_ = byte_;
}
// 3. byte shall be 0x01 or 0x10
if (this->response_message_.size() == 3 && byte_ != 0x01 && byte_ != 0x10) {
this->response_receiving_ = false;
ESP_LOGW(TAG, "Invalid response message: 0x%s. Expected last byte to be 0x01 or 0x10",
PanasonicHelpers::byte_array_to_hex_string(this->response_message_, ','));
delay(10); // NOLINT
continue;
}
void PanasonicHeatpumpComponent::setup()
{
ESP_LOGCONFIG(TAG, "Setting up Panasonic Heatpump ...");
// 4. byte shall be 0x10 or 0x21
if (this->response_message_.size() == 4 && byte_ != 0x10 && byte_ != 0x21) {
this->response_receiving_ = false;
ESP_LOGW(TAG, "Invalid response message: 0x%s. Expected last byte to be 0x10 or 0x21",
PanasonicHelpers::byte_array_to_hex_string(this->response_message_, ','));
delay(10); // NOLINT
this->check_uart_settings(9600, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
this->update();
continue;
}
void PanasonicHeatpumpComponent::update()
{
if (this->uart_client_ != nullptr) return;
this->next_request_ = this->send_extra_request_ ? RequestType::POLLING_EXTRA : RequestType::POLLING;
// Check if message is complete
if (this->response_message_.size() > 2 && this->response_message_.size() == this->payload_length_ + 3) {
this->response_receiving_ = false;
this->current_response_count_++;
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_RX, this->response_message_, ',');
}
}
}
void PanasonicHeatpumpComponent::send_request(RequestType requestType) {
switch (requestType) {
case RequestType::COMMAND:
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_TX, this->command_message_, ',');
this->write_array(this->command_message_);
this->flush();
break;
case RequestType::INITIAL:
// Probably not necessary but CZ-TAW1 sends this query on boot
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE, ',');
this->write_array(PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE);
this->flush();
break;
case RequestType::POLLING:
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::PollingMessage, DATA_MESSAGE_SIZE, ',');
this->write_array(PanasonicCommand::PollingMessage, DATA_MESSAGE_SIZE);
this->flush();
break;
case RequestType::POLLING_EXTRA:
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::PollingExtraMessage, DATA_MESSAGE_SIZE, ',');
this->write_array(PanasonicCommand::PollingExtraMessage, DATA_MESSAGE_SIZE);
this->flush();
break;
};
this->next_request_ = RequestType::NONE;
}
void PanasonicHeatpumpComponent::read_request() {
if (this->uart_client_ == nullptr)
return;
while (this->uart_client_->available()) {
// Read each byte from client and forward it directly to the heatpump
this->uart_client_->read_byte(&byte_);
this->write_byte(byte_);
// Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte
if (!this->request_receiving_) {
if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1)
continue;
this->request_message_.clear();
this->request_receiving_ = true;
}
// Add current byte to message buffer
this->request_message_.push_back(byte_);
// 2. byte contains the payload size
if (this->request_message_.size() == 2) {
this->payload_length_ = byte_;
}
// 3. byte shall be 0x01 or 0x10
if (this->request_message_.size() == 3 && byte_ != 0x01 && byte_ != 0x10) {
this->request_receiving_ = false;
ESP_LOGW(TAG, "Invalid request message: 0x%s. Expected last byte to be 0x01 or 0x10",
PanasonicHelpers::byte_array_to_hex_string(this->request_message_, ','));
delay(10); // NOLINT
continue;
}
// 4. byte shall be 0x10 or 0x21
if (this->request_message_.size() == 4 && byte_ != 0x10 && byte_ != 0x21) {
this->request_receiving_ = false;
ESP_LOGW(TAG, "Invalid request message: 0x%s. Expected last byte to be 0x10 or 0x21",
PanasonicHelpers::byte_array_to_hex_string(this->request_message_, ','));
delay(10); // NOLINT
continue;
}
void PanasonicHeatpumpComponent::loop()
{
switch (this->loop_state_)
{
case LoopState::READ_RESPONSE:
this->read_response();
this->loop_state_ = LoopState::CHECK_RESPONSE;
break;
case LoopState::CHECK_RESPONSE:
this->current_response_ = this->check_response(this->response_message_);
switch (this->current_response_)
{
case ResponseType::UNKNOWN:
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case ResponseType::DEFAULT:
this->loop_state_ = LoopState::PUBLISH_SENSOR;
break;
case ResponseType::EXTRA:
this->loop_state_ = LoopState::PUBLISH_EXTRA_SENSOR;
break;
};
break;
case LoopState::PUBLISH_SENSOR:
for (auto *entity : this->sensors_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_BINARY_SENSOR;
break;
case LoopState::PUBLISH_BINARY_SENSOR:
for (auto *entity : this->binary_sensors_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_TEXT_SENSOR;
break;
case LoopState::PUBLISH_TEXT_SENSOR:
for (auto *entity : this->text_sensors_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_NUMBER;
break;
case LoopState::PUBLISH_NUMBER:
for (auto *entity : this->numbers_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_SELECT;
break;
case LoopState::PUBLISH_SELECT:
for (auto *entity : this->selects_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_SWITCH;
break;
case LoopState::PUBLISH_SWITCH:
for (auto *entity : this->switches_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::PUBLISH_CLIMATE;
break;
case LoopState::PUBLISH_CLIMATE:
for (auto *entity : this->climates_)
{
entity->publish_new_state(this->heatpump_default_message_);
}
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case LoopState::PUBLISH_EXTRA_SENSOR:
for (auto *entity : this->extra_sensors_)
{
entity->publish_new_state(this->heatpump_extra_message_);
}
this->loop_state_ = LoopState::SEND_REQUEST;
break;
case LoopState::SEND_REQUEST:
this->send_request(this->next_request_);
this->loop_state_ = LoopState::READ_REQUEST;
break;
case LoopState::READ_REQUEST:
this->read_request();
this->loop_state_ = LoopState::RESTART_LOOP;
break;
default:
this->loop_state_ = LoopState::READ_RESPONSE;
break;
};
// Check if message is complete
if (this->request_message_.size() > 2 && this->request_message_.size() == this->payload_length_ + 3) {
this->request_receiving_ = false;
if (this->log_uart_msg_)
PanasonicHelpers::log_uart_hex(UART_LOG_TX, this->request_message_, ',');
}
}
}
void PanasonicHeatpumpComponent::read_response()
{
while (this->available())
{
// Read each byte from heatpump and forward it directly to the client (CZ-TAW1)
this->read_byte(&byte_);
if (this->uart_client_ != nullptr)
{
this->uart_client_->write_byte(byte_);
}
int PanasonicHeatpumpComponent::getResponseByte(const int index) {
if (this->heatpump_default_message_.size() > index)
return this->heatpump_default_message_[index];
return -1;
}
// Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte
if (!this->response_receiving_)
{
if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1) continue;
this->response_message_.clear();
this->response_receiving_ = true;
}
// Add current byte to message buffer
this->response_message_.push_back(byte_);
int PanasonicHeatpumpComponent::getExtraResponseByte(const int index) {
if (this->heatpump_extra_message_.size() > index)
return this->heatpump_extra_message_[index];
return -1;
}
// 2. byte contains the payload size
if (this->response_message_.size() == 2)
{
this->payload_length_ = byte_;
}
// 3. byte shall be 0x01 or 0x10
if (this->response_message_.size() == 3 && byte_ != 0x01 && byte_ != 0x10)
{
this->response_receiving_ = false;
ESP_LOGW(TAG, "Invalid response message: 0x%s. Expected last byte to be 0x01 or 0x10",
PanasonicHelpers::byte_array_to_hex_string(this->response_message_, ','));
delay(10); // NOLINT
continue;
}
// 4. byte shall be 0x10 or 0x21
if (this->response_message_.size() == 4 && byte_ != 0x10 && byte_ != 0x21)
{
this->response_receiving_ = false;
ESP_LOGW(TAG, "Invalid response message: 0x%s. Expected last byte to be 0x10 or 0x21",
PanasonicHelpers::byte_array_to_hex_string(this->response_message_, ','));
delay(10); // NOLINT
continue;
}
ResponseType PanasonicHeatpumpComponent::check_response(const std::vector<uint8_t>& data) {
// Read response message:
// format: 0x71 [payload_length] 0x01 [0x10 || 0x21] [[TOP0 - TOP114] ...] 0x00 [checksum]
// payload_length: payload_length + 3 = packet_length
// checksum: if (sum(all bytes) & 0xFF == 0) ==> valid packet
// Check if message is complete
if (this->response_message_.size() > 2 &&
this->response_message_.size() == this->payload_length_ + 3)
{
this->response_receiving_ = false;
this->current_response_count_++;
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_RX, this->response_message_, ',');
}
}
}
if (data.empty())
return ResponseType::UNKNOWN;
if (data[0] != 0x71)
return ResponseType::UNKNOWN;
if (this->response_receiving_)
return ResponseType::UNKNOWN;
if (data.size() != RESPONSE_MSG_SIZE) {
ESP_LOGW(TAG, "Invalid response message length: recieved %d - expected %d", data.size(), RESPONSE_MSG_SIZE);
delay(10); // NOLINT
return ResponseType::UNKNOWN;
}
void PanasonicHeatpumpComponent::send_request(RequestType requestType)
{
switch (requestType)
{
case RequestType::COMMAND:
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, this->command_message_, ',');
this->write_array(this->command_message_);
this->flush();
break;
case RequestType::INITIAL:
// Probably not necessary but CZ-TAW1 sends this query on boot
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE, ',');
this->write_array(PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE);
this->flush();
break;
case RequestType::POLLING:
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::PollingMessage, DATA_MESSAGE_SIZE, ',');
this->write_array(PanasonicCommand::PollingMessage, DATA_MESSAGE_SIZE);
this->flush();
break;
case RequestType::POLLING_EXTRA:
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::PollingExtraMessage, DATA_MESSAGE_SIZE, ',');
this->write_array(PanasonicCommand::PollingExtraMessage, DATA_MESSAGE_SIZE);
this->flush();
break;
};
// Verify checksum
uint8_t checksum = 0;
for (int i = 0; i < data.size(); i++) {
checksum += data[i];
}
// all bytes (including checksum byte) shall be 0x00
if (checksum != 0) {
ESP_LOGW(TAG, "Invalid response message: checksum = 0x%02X, last_byte = 0x%02X", checksum, data[202]);
delay(10); // NOLINT
return ResponseType::UNKNOWN;
}
this->next_request_ = RequestType::NONE;
}
this->send_extra_request_ = data[3] == 0x10 && data[199] > 0x02 && this->send_extra_request_ == false ? true : false;
void PanasonicHeatpumpComponent::read_request()
{
if (this->uart_client_ == nullptr) return;
// Get response type and save the response
auto responseType = ResponseType::UNKNOWN;
if (data[3] == 0x10) {
responseType = ResponseType::STANDARD;
this->heatpump_default_message_ = data;
} else if (data[3] == 0x21) {
responseType = ResponseType::EXTRA;
this->heatpump_extra_message_ = data;
}
if (responseType == ResponseType::UNKNOWN) {
ESP_LOGW(TAG, "Unknown response type (4. byte): 0x%02X. Expected 0x10 or 0x21.", data[3]);
delay(10); // NOLINT
return responseType;
}
while (this->uart_client_->available())
{
// Read each byte from client and forward it directly to the heatpump
this->uart_client_->read_byte(&byte_);
this->write_byte(byte_);
// Check if the current response is a new response
if (this->last_response_count_ == this->current_response_count_)
return ResponseType::UNKNOWN;
this->last_response_count_ = this->current_response_count_;
// Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte
if (!this->request_receiving_)
{
if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1) continue;
this->request_message_.clear();
this->request_receiving_ = true;
}
// Add current byte to message buffer
this->request_message_.push_back(byte_);
return responseType;
}
// 2. byte contains the payload size
if (this->request_message_.size() == 2)
{
this->payload_length_ = byte_;
}
// 3. byte shall be 0x01 or 0x10
if (this->request_message_.size() == 3 && byte_ != 0x01 && byte_ != 0x10)
{
this->request_receiving_ = false;
ESP_LOGW(TAG, "Invalid request message: 0x%s. Expected last byte to be 0x01 or 0x10",
PanasonicHelpers::byte_array_to_hex_string(this->request_message_, ','));
delay(10); // NOLINT
continue;
}
// 4. byte shall be 0x10 or 0x21
if (this->request_message_.size() == 4 && byte_ != 0x10 && byte_ != 0x21)
{
this->request_receiving_ = false;
ESP_LOGW(TAG, "Invalid request message: 0x%s. Expected last byte to be 0x10 or 0x21",
PanasonicHelpers::byte_array_to_hex_string(this->request_message_, ','));
delay(10); // NOLINT
continue;
}
void PanasonicHeatpumpComponent::set_command_high_nibble(const uint8_t value, const uint8_t index) {
if (this->next_request_ != RequestType::COMMAND) {
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
uint8_t lowNibble = this->heatpump_default_message_[index] & 0b1111;
uint8_t highNibble = value << 4;
// set command byte
this->command_message_[index] = highNibble + lowNibble;
// calculate and set set checksum (last element)
this->command_message_.back() =
PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// Check if message is complete
if (this->request_message_.size() > 2 &&
this->request_message_.size() == this->payload_length_ + 3)
{
this->request_receiving_ = false;
if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, this->request_message_, ',');
}
}
}
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
int PanasonicHeatpumpComponent::getResponseByte(const int index)
{
if (this->heatpump_default_message_.size() > index) return this->heatpump_default_message_[index];
return -1;
}
void PanasonicHeatpumpComponent::set_command_low_nibble(const uint8_t value, const uint8_t index) {
if (this->next_request_ != RequestType::COMMAND) {
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
uint8_t highNibble = this->heatpump_default_message_[index] & 0b11110000;
uint8_t lowNibble = value & 0b1111;
// set command byte
this->command_message_[index] = highNibble + lowNibble;
// calculate and set set checksum (last element)
this->command_message_.back() =
PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
int PanasonicHeatpumpComponent::getExtraResponseByte(const int index)
{
if (this->heatpump_extra_message_.size() > index) return this->heatpump_extra_message_[index];
return -1;
}
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
ResponseType PanasonicHeatpumpComponent::check_response(const std::vector<uint8_t>& data)
{
// Read response message:
// format: 0x71 [payload_length] 0x01 [0x10 || 0x21] [[TOP0 - TOP114] ...] 0x00 [checksum]
// payload_length: payload_length + 3 = packet_length
// checksum: if (sum(all bytes) & 0xFF == 0) ==> valid packet
void PanasonicHeatpumpComponent::set_command_byte(const uint8_t value, const uint8_t index) {
if (this->next_request_ != RequestType::COMMAND) {
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
// set command byte
this->command_message_[index] = value;
// calculate and set set checksum (last element)
this->command_message_.back() =
PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
if (data.empty()) return ResponseType::UNKNOWN;
if (data[0] != 0x71) return ResponseType::UNKNOWN;
if (this->response_receiving_) return ResponseType::UNKNOWN;
if (data.size() != RESPONSE_MSG_SIZE)
{
ESP_LOGW(TAG, "Invalid response message length: recieved %d - expected %d", data.size(), RESPONSE_MSG_SIZE);
delay(10); // NOLINT
return ResponseType::UNKNOWN;
}
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
// Verify checksum
uint8_t checksum = 0;
for (int i = 0; i < data.size(); i++)
{
checksum += data[i];
}
// all bytes (including checksum byte) shall be 0x00
if (checksum != 0)
{
ESP_LOGW(TAG, "Invalid response message: checksum = 0x%02X, last_byte = 0x%02X", checksum, data[202]);
delay(10); // NOLINT
return ResponseType::UNKNOWN;
}
void PanasonicHeatpumpComponent::set_command_curve(const uint8_t value, const uint8_t index) {
if (this->next_request_ != RequestType::COMMAND) {
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
this->send_extra_request_ = data[3] == 0x10 && data[199] > 0x02 &&
this->send_extra_request_ == false ? true : false;
// Set zone 1 curve bytes
if (index == 75 || index == 76 || index == 77 || index == 78 || index == 86 || index == 87 || index == 88 ||
index == 89) {
this->command_message_[75] = this->heatpump_default_message_[75];
this->command_message_[76] = this->heatpump_default_message_[76];
this->command_message_[77] = this->heatpump_default_message_[77];
this->command_message_[78] = this->heatpump_default_message_[78];
this->command_message_[86] = this->heatpump_default_message_[86];
this->command_message_[87] = this->heatpump_default_message_[87];
this->command_message_[88] = this->heatpump_default_message_[88];
this->command_message_[89] = this->heatpump_default_message_[89];
}
// Set zone 2 curve bytes
if (index == 79 || index == 80 || index == 81 || index == 82 || index == 90 || index == 91 || index == 92 ||
index == 93) {
this->command_message_[79] = this->heatpump_default_message_[79];
this->command_message_[80] = this->heatpump_default_message_[80];
this->command_message_[81] = this->heatpump_default_message_[81];
this->command_message_[82] = this->heatpump_default_message_[82];
this->command_message_[90] = this->heatpump_default_message_[90];
this->command_message_[91] = this->heatpump_default_message_[91];
this->command_message_[92] = this->heatpump_default_message_[92];
this->command_message_[93] = this->heatpump_default_message_[93];
}
// Get response type and save the response
auto responseType = ResponseType::UNKNOWN;
if (data[3] == 0x10)
{
responseType = ResponseType::DEFAULT;
this->heatpump_default_message_ = data;
}
else if (data[3] == 0x21)
{
responseType = ResponseType::EXTRA;
this->heatpump_extra_message_ = data;
}
if (responseType == ResponseType::UNKNOWN)
{
ESP_LOGW(TAG, "Unknown response type (4. byte): 0x%02X. Expected 0x10 or 0x21.", data[3]);
delay(10); // NOLINT
return responseType;
}
// set command byte
this->command_message_[index] = value;
// calculate and set set checksum (last element)
this->command_message_.back() =
PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// Check if the current response is a new response
if (this->last_response_count_ == this->current_response_count_) return ResponseType::UNKNOWN;
this->last_response_count_ = this->current_response_count_;
return responseType;
}
void PanasonicHeatpumpComponent::set_command_high_nibble(const uint8_t value, const uint8_t index)
{
if (this->next_request_ != RequestType::COMMAND)
{
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
uint8_t lowNibble = this->heatpump_default_message_[index] & 0b1111;
uint8_t highNibble = value << 4;
// set command byte
this->command_message_[index] = highNibble + lowNibble;
// calculate and set set checksum (last element)
this->command_message_.back() = PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
void PanasonicHeatpumpComponent::set_command_low_nibble(const uint8_t value, const uint8_t index)
{
if (this->next_request_ != RequestType::COMMAND)
{
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
uint8_t highNibble = this->heatpump_default_message_[index] & 0b11110000;
uint8_t lowNibble = value & 0b1111;
// set command byte
this->command_message_[index] = highNibble + lowNibble;
// calculate and set set checksum (last element)
this->command_message_.back() = PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
void PanasonicHeatpumpComponent::set_command_byte(const uint8_t value, const uint8_t index)
{
if (this->next_request_ != RequestType::COMMAND)
{
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
// set command byte
this->command_message_[index] = value;
// calculate and set set checksum (last element)
this->command_message_.back() = PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
void PanasonicHeatpumpComponent::set_command_curve(const uint8_t value, const uint8_t index)
{
if (this->next_request_ != RequestType::COMMAND)
{
// initialize the command
this->command_message_.assign(std::begin(PanasonicCommand::CommandMessage),
std::end(PanasonicCommand::CommandMessage));
}
// Set zone 1 curve bytes
if (index == 75 || index == 76 || index == 77 || index == 78 ||
index == 86 || index == 87 || index == 88 || index == 89)
{
this->command_message_[75] = this->heatpump_default_message_[75];
this->command_message_[76] = this->heatpump_default_message_[76];
this->command_message_[77] = this->heatpump_default_message_[77];
this->command_message_[78] = this->heatpump_default_message_[78];
this->command_message_[86] = this->heatpump_default_message_[86];
this->command_message_[87] = this->heatpump_default_message_[87];
this->command_message_[88] = this->heatpump_default_message_[88];
this->command_message_[89] = this->heatpump_default_message_[89];
}
// Set zone 2 curve bytes
if (index == 79 || index == 80 || index == 81 || index == 82 ||
index == 90 || index == 91 || index == 92 || index == 93)
{
this->command_message_[79] = this->heatpump_default_message_[79];
this->command_message_[80] = this->heatpump_default_message_[80];
this->command_message_[81] = this->heatpump_default_message_[81];
this->command_message_[82] = this->heatpump_default_message_[82];
this->command_message_[90] = this->heatpump_default_message_[90];
this->command_message_[91] = this->heatpump_default_message_[91];
this->command_message_[92] = this->heatpump_default_message_[92];
this->command_message_[93] = this->heatpump_default_message_[93];
}
// set command byte
this->command_message_[index] = value;
// calculate and set set checksum (last element)
this->command_message_.back() = PanasonicCommand::calcChecksum(this->command_message_, this->command_message_.size() - 1);
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
} // namespace panasonic_heatpump
// command will be send on next loop
this->next_request_ = RequestType::COMMAND;
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -11,123 +11,141 @@
#include "decode.h"
#include "commands.h"
#ifndef PANASONIC_HEATPUMP_VERSION
#define PANASONIC_HEATPUMP_VERSION "0.0.5"
#endif
namespace esphome
{
namespace panasonic_heatpump
{
enum LoopState : uint8_t
{
READ_RESPONSE,
CHECK_RESPONSE,
PUBLISH_SENSOR,
PUBLISH_BINARY_SENSOR,
PUBLISH_TEXT_SENSOR,
PUBLISH_NUMBER,
PUBLISH_SELECT,
PUBLISH_SWITCH,
PUBLISH_CLIMATE,
PUBLISH_EXTRA_SENSOR,
SEND_REQUEST,
READ_REQUEST,
RESTART_LOOP,
};
namespace esphome {
namespace panasonic_heatpump {
enum LoopState : uint8_t {
READ_RESPONSE,
CHECK_RESPONSE,
PUBLISH_SENSOR,
PUBLISH_BINARY_SENSOR,
PUBLISH_TEXT_SENSOR,
PUBLISH_NUMBER,
PUBLISH_SELECT,
PUBLISH_SWITCH,
PUBLISH_CLIMATE,
PUBLISH_EXTRA_SENSOR,
SEND_REQUEST,
READ_REQUEST,
RESTART_LOOP,
};
enum RequestType : uint8_t
{
NONE,
COMMAND,
INITIAL,
POLLING,
POLLING_EXTRA,
};
enum RequestType : uint8_t {
NONE,
COMMAND,
INITIAL,
POLLING,
POLLING_EXTRA,
};
enum ResponseType : uint8_t
{
UNKNOWN,
DEFAULT,
EXTRA,
};
enum ResponseType : uint8_t {
UNKNOWN,
STANDARD,
EXTRA,
};
class PanasonicHeatpumpEntity
{
public:
virtual void set_id(const int id) { id_ = id; }
virtual void publish_new_state(const std::vector<uint8_t>& data) = 0;
class PanasonicHeatpumpEntity {
public:
virtual void set_id(const int id) {
id_ = id;
}
virtual void publish_new_state(const std::vector<uint8_t>& data) = 0;
protected:
int id_ { -1 };
int keep_state_ { 0 };
};
protected:
int id_{-1};
int keep_state_{0};
};
class PanasonicHeatpumpComponent : public PollingComponent, public uart::UARTDevice
{
public:
PanasonicHeatpumpComponent() = default;
// base class functions
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
void setup() override;
void update() override;
void loop() override;
// option functions
void set_uart_client(uart::UARTComponent* uart) { this->uart_client_ = uart; }
void set_log_uart_msg(bool active) { this->log_uart_msg_ = active; }
// uart message variables to use in lambda functions
int getResponseByte(const int index);
int getExtraResponseByte(const int index);
// command functions
void set_command_high_nibble(const uint8_t value, const uint8_t index);
void set_command_low_nibble(const uint8_t value, const uint8_t index);
void set_command_byte(const uint8_t value, const uint8_t index);
void set_command_curve(const uint8_t value, const uint8_t index);
// entity functions
void add_binary_sensor(PanasonicHeatpumpEntity *binary_sensor) { binary_sensors_.push_back(binary_sensor); }
void add_climate(PanasonicHeatpumpEntity *climate) { climates_.push_back(climate); }
void add_number(PanasonicHeatpumpEntity *number) { numbers_.push_back(number); }
void add_select(PanasonicHeatpumpEntity *select) { selects_.push_back(select); }
void add_sensor(PanasonicHeatpumpEntity *sensor) { sensors_.push_back(sensor); }
void add_switch(PanasonicHeatpumpEntity *switch_) { switches_.push_back(switch_); }
void add_text_sensor(PanasonicHeatpumpEntity *text_sensor) { text_sensors_.push_back(text_sensor); }
void add_extra_sensor(PanasonicHeatpumpEntity *sensor) { extra_sensors_.push_back(sensor); }
class PanasonicHeatpumpComponent : public PollingComponent, public uart::UARTDevice {
public:
PanasonicHeatpumpComponent() = default;
// base class functions
float get_setup_priority() const override {
return setup_priority::DATA;
}
void dump_config() override;
void setup() override;
void update() override;
void loop() override;
// option functions
void set_uart_client(uart::UARTComponent* uart) {
this->uart_client_ = uart;
}
void set_log_uart_msg(bool active) {
this->log_uart_msg_ = active;
}
// uart message variables to use in lambda functions
int getResponseByte(const int index);
int getExtraResponseByte(const int index);
// command functions
void set_command_high_nibble(const uint8_t value, const uint8_t index);
void set_command_low_nibble(const uint8_t value, const uint8_t index);
void set_command_byte(const uint8_t value, const uint8_t index);
void set_command_curve(const uint8_t value, const uint8_t index);
// entity functions
void add_binary_sensor(PanasonicHeatpumpEntity* binary_sensor) {
binary_sensors_.push_back(binary_sensor);
}
void add_climate(PanasonicHeatpumpEntity* climate) {
climates_.push_back(climate);
}
void add_number(PanasonicHeatpumpEntity* number) {
numbers_.push_back(number);
}
void add_select(PanasonicHeatpumpEntity* select) {
selects_.push_back(select);
}
void add_sensor(PanasonicHeatpumpEntity* sensor) {
sensors_.push_back(sensor);
}
void add_switch(PanasonicHeatpumpEntity* switch_) {
switches_.push_back(switch_);
}
void add_text_sensor(PanasonicHeatpumpEntity* text_sensor) {
text_sensors_.push_back(text_sensor);
}
void add_extra_sensor(PanasonicHeatpumpEntity* sensor) {
extra_sensors_.push_back(sensor);
}
protected:
const std::string version = "0.0.5";
// options variables
uart::UARTComponent* uart_client_ { nullptr };
bool log_uart_msg_ { false };
// uart message variables
std::vector<uint8_t> heatpump_default_message_;
std::vector<uint8_t> heatpump_extra_message_;
std::vector<uint8_t> response_message_;
std::vector<uint8_t> request_message_;
std::vector<uint8_t> command_message_;
uint8_t payload_length_;
uint8_t byte_;
uint8_t current_response_count_ { 0 };
uint8_t last_response_count_ { 0 };
bool response_receiving_ { false };
bool request_receiving_ { false };
bool send_extra_request_ { false };
LoopState loop_state_ { LoopState::RESTART_LOOP };
RequestType next_request_ { RequestType::INITIAL };
ResponseType current_response_ { ResponseType::UNKNOWN };
// entity vectors
std::vector<PanasonicHeatpumpEntity *> binary_sensors_;
std::vector<PanasonicHeatpumpEntity *> climates_;
std::vector<PanasonicHeatpumpEntity *> numbers_;
std::vector<PanasonicHeatpumpEntity *> selects_;
std::vector<PanasonicHeatpumpEntity *> sensors_;
std::vector<PanasonicHeatpumpEntity *> switches_;
std::vector<PanasonicHeatpumpEntity *> text_sensors_;
std::vector<PanasonicHeatpumpEntity *> extra_sensors_;
protected:
// options variables
uart::UARTComponent* uart_client_{nullptr};
bool log_uart_msg_{false};
// uart message variables
std::vector<uint8_t> heatpump_default_message_;
std::vector<uint8_t> heatpump_extra_message_;
std::vector<uint8_t> response_message_;
std::vector<uint8_t> request_message_;
std::vector<uint8_t> command_message_;
uint8_t payload_length_;
uint8_t byte_;
uint8_t current_response_count_{0};
uint8_t last_response_count_{0};
bool response_receiving_{false};
bool request_receiving_{false};
bool send_extra_request_{false};
LoopState loop_state_{LoopState::RESTART_LOOP};
RequestType next_request_{RequestType::INITIAL};
ResponseType current_response_{ResponseType::UNKNOWN};
// entity vectors
std::vector<PanasonicHeatpumpEntity*> binary_sensors_;
std::vector<PanasonicHeatpumpEntity*> climates_;
std::vector<PanasonicHeatpumpEntity*> numbers_;
std::vector<PanasonicHeatpumpEntity*> selects_;
std::vector<PanasonicHeatpumpEntity*> sensors_;
std::vector<PanasonicHeatpumpEntity*> switches_;
std::vector<PanasonicHeatpumpEntity*> text_sensors_;
std::vector<PanasonicHeatpumpEntity*> extra_sensors_;
// uart message functions
void read_response();
void send_request(RequestType requestType);
void read_request();
ResponseType check_response(const std::vector<uint8_t>& data);
};
} // namespace panasonic_heatpump
// uart message functions
void read_response();
void send_request(RequestType requestType);
void read_request();
ResponseType check_response(const std::vector<uint8_t>& data);
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,11 +1,11 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import select
from esphome.const import (
ENTITY_CATEGORY_CONFIG,
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
CONF_COOL_MODE = "cool_mode"
CONF_SET2 = "set2" # Set Holiday Mode
@ -18,67 +18,104 @@ CONF_SET26 = "set26" # Set External PadHeater
CONF_SET35 = "set35" # Set Bivalent Mode
TYPES = [
CONF_SET2,
CONF_SET3,
CONF_SET4,
CONF_SET9,
CONF_SET17,
CONF_SET26,
CONF_SET35,
CONF_SET2,
CONF_SET3,
CONF_SET4,
CONF_SET9,
CONF_SET17,
CONF_SET26,
CONF_SET35,
]
CONF_SELECTS = [
[ "Off", "Scheduled", "Active", ],
[ "Off", "Level 1", "Level 2", "Level 3", ],
[ "Off", "30min", "60min", "90min", ],
[ "TANK", "HEAT", "HEAT+TANK", ],
[ "Zone 1", "Zone 2", "Zone 1 & 2", ],
[ "Disabled", "Type-A", "Type-B" ],
[ "Alternative", "Parallel", "Advanced Parallel" ],
[
"Off",
"Scheduled",
"Active",
],
[
"Off",
"Level 1",
"Level 2",
"Level 3",
],
[
"Off",
"30min",
"60min",
"90min",
],
[
"TANK",
"HEAT",
"HEAT+TANK",
],
[
"Zone 1",
"Zone 2",
"Zone 1 & 2",
],
["Disabled", "Type-A", "Type-B"],
["Alternative", "Parallel", "Advanced Parallel"],
]
CONF_SELECT_COOL_MODE = [ "TANK", "HEAT", "HEAT+TANK", "COOL", "COOL+TANK", "AUTO", "AUTO+TANK", ]
PanasonicHeatpumpSelect = panasonic_heatpump_ns.class_("PanasonicHeatpumpSelect", select.Select, cg.Component)
CONF_SELECT_COOL_MODE = [
"TANK",
"HEAT",
"HEAT+TANK",
"COOL",
"COOL+TANK",
"AUTO",
"AUTO+TANK",
]
PanasonicHeatpumpSelect = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpSelect", select.Select, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_COOL_MODE, default=False): cv.boolean,
cv.Optional(CONF_SET2): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET3): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET4): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET9): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET17): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET26): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET35): select.select_schema(
PanasonicHeatpumpSelect,
),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_COOL_MODE, default=False): cv.boolean,
cv.Optional(CONF_SET2): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET3): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET4): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET9): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET17): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET26): select.select_schema(
PanasonicHeatpumpSelect,
),
cv.Optional(CONF_SET35): select.select_schema(
PanasonicHeatpumpSelect,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
child_options = CONF_SELECT_COOL_MODE if config[CONF_COOL_MODE] and key == CONF_SET9 else CONF_SELECTS[index]
var = await select.new_select(child_config, options=child_options)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_select(var))
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
child_options = (
CONF_SELECT_COOL_MODE
if config[CONF_COOL_MODE] and key == CONF_SET9
else CONF_SELECTS[index]
)
var = await select.new_select(child_config, options=child_options)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_select(var))

View File

@ -1,98 +1,102 @@
#include "panasonic_heatpump_select.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.select";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.select";
void PanasonicHeatpumpSelect::dump_config() {
LOG_SELECT("", "Panasonic Heatpump Select", this);
delay(10);
}
void PanasonicHeatpumpSelect::dump_config()
{
LOG_SELECT("", "Panasonic Heatpump Select", this);
delay(10);
}
void PanasonicHeatpumpSelect::control(const std::string& value) {
auto optIndex = this->index_of(value);
if (!optIndex.has_value())
return;
size_t index = optIndex.value();
void PanasonicHeatpumpSelect::control(const std::string &value)
{
auto optIndex = this->index_of(value);
if (!optIndex.has_value()) return;
size_t index = optIndex.value();
switch (this->id_) {
case SelectIds::CONF_SET2:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(index), 5);
break;
case SelectIds::CONF_SET3:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply8(index), 7);
break;
case SelectIds::CONF_SET4:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(index), 7);
break;
case SelectIds::CONF_SET9:
this->parent_->set_command_byte(PanasonicCommand::setOperationMode(index), 6);
break;
case SelectIds::CONF_SET17:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(index), 6);
break;
case SelectIds::CONF_SET26:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(index), 25);
break;
case SelectIds::CONF_SET35:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(index), 26);
break;
default:
return;
};
switch (this->id_)
{
case SelectIds::CONF_SET2:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(index), 5);
break;
case SelectIds::CONF_SET3:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply8(index), 7);
break;
case SelectIds::CONF_SET4:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(index), 7);
break;
case SelectIds::CONF_SET9:
this->parent_->set_command_byte(PanasonicCommand::setOperationMode(index), 6);
break;
case SelectIds::CONF_SET17:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(index), 6);
break;
case SelectIds::CONF_SET26:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(index), 25);
break;
case SelectIds::CONF_SET35:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(index), 26);
break;
default: return;
};
this->publish_state(value);
this->keep_state_ = 2;
}
this->publish_state(value);
this->keep_state_ = 2;
}
void PanasonicHeatpumpSelect::publish_new_state(const std::vector<uint8_t>& data) {
if (this->keep_state_ > 0) {
this->keep_state_--;
return;
}
if (data.empty())
return;
void PanasonicHeatpumpSelect::publish_new_state(const std::vector<uint8_t>& data)
{
if (this->keep_state_ > 0)
{
this->keep_state_--;
return;
}
if (data.empty()) return;
std::string new_state;
switch (this->id_) {
case SelectIds::CONF_SET9:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::OperationMode, PanasonicDecode::getOperationMode(data[6]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET4:
new_state = PanasonicDecode::getTextState(PanasonicDecode::PowerfulMode, PanasonicDecode::getBit6and7and8(data[7]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET3:
new_state = PanasonicDecode::getTextState(PanasonicDecode::QuietMode, PanasonicDecode::getBit3and4and5(data[7]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET2:
new_state = PanasonicDecode::getTextState(PanasonicDecode::HolidayState, PanasonicDecode::getBit3and4(data[5]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET17:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneState, PanasonicDecode::getBit1and2(data[6]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET26:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::ExtPadHeaterType, PanasonicDecode::getBit3and4(data[25]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
case SelectIds::CONF_SET35:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BivalentMode, PanasonicDecode::getBit5and6(data[26]));
if (this->has_state() && this->current_option() == new_state)
return;
break;
default:
return;
};
std::string new_state;
switch (this->id_)
{
case SelectIds::CONF_SET9:
new_state = PanasonicDecode::getTextState(PanasonicDecode::OperationMode, PanasonicDecode::getOperationMode(data[6]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET4:
new_state = PanasonicDecode::getTextState(PanasonicDecode::PowerfulMode, PanasonicDecode::getBit6and7and8(data[7]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET3:
new_state = PanasonicDecode::getTextState(PanasonicDecode::QuietMode, PanasonicDecode::getBit3and4and5(data[7]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET2:
new_state = PanasonicDecode::getTextState(PanasonicDecode::HolidayState, PanasonicDecode::getBit3and4(data[5]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET17:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneState, PanasonicDecode::getBit1and2(data[6]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET26:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ExtPadHeaterType, PanasonicDecode::getBit3and4(data[25]));
if (this->has_state() && this->current_option() == new_state) return;
break;
case SelectIds::CONF_SET35:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BivalentMode, PanasonicDecode::getBit5and6(data[26]));
if (this->has_state() && this->current_option() == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -5,32 +5,29 @@
#include "../decode.h"
#include "../commands.h"
namespace esphome {
namespace panasonic_heatpump {
enum SelectIds : uint8_t {
CONF_SET2,
CONF_SET3,
CONF_SET4,
CONF_SET9,
CONF_SET17,
CONF_SET26,
CONF_SET35,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum SelectIds : uint8_t
{
CONF_SET2,
CONF_SET3,
CONF_SET4,
CONF_SET9,
CONF_SET17,
CONF_SET26,
CONF_SET35,
};
class PanasonicHeatpumpSelect : public select::Select,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpSelect() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
class PanasonicHeatpumpSelect : public select::Select, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpSelect() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
protected:
void control(const std::string &value) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
protected:
void control(const std::string& value) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

File diff suppressed because it is too large Load Diff

View File

@ -1,434 +1,531 @@
#include "panasonic_heatpump_sensor.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.sensor";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.sensor";
void PanasonicHeatpumpSensor::dump_config() {
LOG_SENSOR("", "Panasonic Heatpump Sensor", this);
delay(10);
}
void PanasonicHeatpumpSensor::dump_config()
{
LOG_SENSOR("", "Panasonic Heatpump Sensor", this);
delay(10);
}
void PanasonicHeatpumpSensor::publish_new_state(const std::vector<uint8_t>& data) {
if (data.empty())
return;
void PanasonicHeatpumpSensor::publish_new_state(const std::vector<uint8_t>& data)
{
if (data.empty()) return;
float new_state;
switch (this->id_) {
case SensorIds::CONF_TOP1:
new_state = PanasonicDecode::getPumpFlow(data, 169);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP5:
new_state = PanasonicDecode::getByteMinus128(data[143]) + PanasonicDecode::getFractional(data[118], 0);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP6:
new_state = PanasonicDecode::getByteMinus128(data[144]) + PanasonicDecode::getFractional(data[118], 3);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP7:
new_state = PanasonicDecode::getByteMinus128(data[153]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP8:
new_state = PanasonicDecode::getByteMinus1(data[166]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP9:
new_state = PanasonicDecode::getByteMinus128(data[42]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP10:
new_state = PanasonicDecode::getByteMinus128(data[141]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP11:
new_state = PanasonicDecode::getWordMinus1(data, 182);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP12:
new_state = PanasonicDecode::getWordMinus1(data, 179);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP14:
new_state = PanasonicDecode::getByteMinus128(data[142]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP15:
new_state = PanasonicDecode::getByteMinus1Times200(data[194]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP16:
new_state = PanasonicDecode::getByteMinus1Times200(data[193]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP21:
new_state = PanasonicDecode::getByteMinus128(data[158]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP22:
new_state = PanasonicDecode::getByteMinus128(data[99]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP23:
new_state = PanasonicDecode::getByteMinus128(data[84]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP24:
new_state = PanasonicDecode::getByteMinus128(data[94]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP25:
new_state = PanasonicDecode::getByteMinus128(data[44]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP27:
new_state = PanasonicDecode::getByteMinus128(data[38]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP28:
new_state = PanasonicDecode::getByteMinus128(data[39]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP29:
new_state = PanasonicDecode::getByteMinus128(data[75]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP30:
new_state = PanasonicDecode::getByteMinus128(data[76]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP31:
new_state = PanasonicDecode::getByteMinus128(data[78]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP32:
new_state = PanasonicDecode::getByteMinus128(data[77]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP33:
new_state = PanasonicDecode::getByteMinus128(data[156]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP34:
new_state = PanasonicDecode::getByteMinus128(data[40]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP35:
new_state = PanasonicDecode::getByteMinus128(data[41]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP36:
new_state = PanasonicDecode::getByteMinus128(data[145]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP37:
new_state = PanasonicDecode::getByteMinus128(data[146]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP38:
new_state = PanasonicDecode::getByteMinus1Times200(data[196]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP39:
new_state = PanasonicDecode::getByteMinus1Times200(data[195]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP40:
new_state = PanasonicDecode::getByteMinus1Times200(data[198]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP41:
new_state = PanasonicDecode::getByteMinus1Times200(data[197]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP42:
new_state = PanasonicDecode::getByteMinus128(data[147]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP43:
new_state = PanasonicDecode::getByteMinus128(data[148]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP45:
new_state = PanasonicDecode::getByteMinus128(data[43]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP46:
new_state = PanasonicDecode::getByteMinus128(data[149]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP47:
new_state = PanasonicDecode::getByteMinus128(data[150]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP48:
new_state = PanasonicDecode::getByteMinus128(data[151]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP49:
new_state = PanasonicDecode::getByteMinus128(data[154]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP50:
new_state = PanasonicDecode::getByteMinus128(data[155]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP51:
new_state = PanasonicDecode::getByteMinus128(data[157]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP52:
new_state = PanasonicDecode::getByteMinus128(data[159]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP53:
new_state = PanasonicDecode::getByteMinus128(data[160]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP54:
new_state = PanasonicDecode::getByteMinus128(data[161]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP55:
new_state = PanasonicDecode::getByteMinus128(data[162]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP56:
new_state = PanasonicDecode::getByteMinus128(data[139]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP57:
new_state = PanasonicDecode::getByteMinus128(data[140]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP62:
new_state = PanasonicDecode::getByteMinus1Times10(data[173]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP63:
new_state = PanasonicDecode::getByteMinus1Times10(data[174]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP64:
new_state = PanasonicDecode::getByteMinus1Div5(data[163]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP65:
new_state = PanasonicDecode::getByteMinus1Times50(data[171]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP66:
new_state = PanasonicDecode::getByteMinus1Times50(data[164]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP67:
new_state = PanasonicDecode::getByteMinus1Div5(data[165]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP70:
new_state = PanasonicDecode::getByteMinus128(data[100]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP71:
new_state = PanasonicDecode::getByteMinus1(data[101]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP72:
new_state = PanasonicDecode::getByteMinus128(data[86]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP73:
new_state = PanasonicDecode::getByteMinus128(data[87]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP74:
new_state = PanasonicDecode::getByteMinus128(data[89]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP75:
new_state = PanasonicDecode::getByteMinus128(data[88]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP77:
new_state = PanasonicDecode::getByteMinus128(data[83]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP78:
new_state = PanasonicDecode::getByteMinus128(data[85]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP79:
new_state = PanasonicDecode::getByteMinus128(data[95]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP80:
new_state = PanasonicDecode::getByteMinus128(data[96]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP82:
new_state = PanasonicDecode::getByteMinus128(data[79]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP83:
new_state = PanasonicDecode::getByteMinus128(data[80]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP84:
new_state = PanasonicDecode::getByteMinus128(data[82]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP85:
new_state = PanasonicDecode::getByteMinus128(data[81]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP86:
new_state = PanasonicDecode::getByteMinus128(data[90]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP87:
new_state = PanasonicDecode::getByteMinus128(data[91]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP88:
new_state = PanasonicDecode::getByteMinus128(data[93]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP89:
new_state = PanasonicDecode::getByteMinus128(data[92]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP90:
new_state = PanasonicDecode::getWordMinus1(data, 185);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP91:
new_state = PanasonicDecode::getWordMinus1(data, 188);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP93:
new_state = PanasonicDecode::getByteMinus1(data[172]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP95:
new_state = PanasonicDecode::getByteMinus1(data[45]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP96:
new_state = PanasonicDecode::getByteMinus1(data[104]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP97:
new_state = PanasonicDecode::getByteMinus128(data[105]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP98:
new_state = PanasonicDecode::getByteMinus128(data[106]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP102:
new_state = PanasonicDecode::getByteMinus128(data[61]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP103:
new_state = PanasonicDecode::getByteMinus128(data[62]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP104:
new_state = PanasonicDecode::getByteMinus128(data[63]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP105:
new_state = PanasonicDecode::getByteMinus128(data[64]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP113:
new_state = PanasonicDecode::getByteMinus128(data[59]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP115:
new_state = PanasonicDecode::getByteMinus1Div50(data[125]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP116:
new_state = PanasonicDecode::getByteMinus128(data[126]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP117:
new_state = PanasonicDecode::getByteMinus128(data[127]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP118:
new_state = PanasonicDecode::getByteMinus128(data[128]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP127:
new_state = PanasonicDecode::getByteMinus1Div2(data[177]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP128:
new_state = PanasonicDecode::getByteMinus1Div2(data[178]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP131:
new_state = PanasonicDecode::getByteMinus128(data[65]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP134:
new_state = PanasonicDecode::getByteMinus128(data[66]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP135:
new_state = PanasonicDecode::getByteMinus128(data[68]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP136:
new_state = PanasonicDecode::getByteMinus1(data[67]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP137:
new_state = PanasonicDecode::getByteMinus1(data[69]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_TOP138:
new_state = PanasonicDecode::getByteMinus1(data[70]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
float new_state;
switch (this->id_)
{
case SensorIds::CONF_TOP1:
new_state = PanasonicDecode::getPumpFlow(data, 169);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP5:
new_state = PanasonicDecode::getByteMinus128(data[143]) + PanasonicDecode::getFractional(data[118], 0);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP6:
new_state = PanasonicDecode::getByteMinus128(data[144]) + PanasonicDecode::getFractional(data[118], 3);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP7:
new_state = PanasonicDecode::getByteMinus128(data[153]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP8:
new_state = PanasonicDecode::getByteMinus1(data[166]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP9:
new_state = PanasonicDecode::getByteMinus128(data[42]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP10:
new_state = PanasonicDecode::getByteMinus128(data[141]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP11:
new_state = PanasonicDecode::getWordMinus1(data, 182);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP12:
new_state = PanasonicDecode::getWordMinus1(data, 179);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP14:
new_state = PanasonicDecode::getByteMinus128(data[142]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP15:
new_state = PanasonicDecode::getByteMinus1Times200(data[194]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP16:
new_state = PanasonicDecode::getByteMinus1Times200(data[193]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP21:
new_state = PanasonicDecode::getByteMinus128(data[158]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP22:
new_state = PanasonicDecode::getByteMinus128(data[99]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP23:
new_state = PanasonicDecode::getByteMinus128(data[84]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP24:
new_state = PanasonicDecode::getByteMinus128(data[94]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP25:
new_state = PanasonicDecode::getByteMinus128(data[44]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP27:
new_state = PanasonicDecode::getByteMinus128(data[38]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP28:
new_state = PanasonicDecode::getByteMinus128(data[39]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP29:
new_state = PanasonicDecode::getByteMinus128(data[75]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP30:
new_state = PanasonicDecode::getByteMinus128(data[76]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP31:
new_state = PanasonicDecode::getByteMinus128(data[78]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP32:
new_state = PanasonicDecode::getByteMinus128(data[77]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP33:
new_state = PanasonicDecode::getByteMinus128(data[156]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP34:
new_state = PanasonicDecode::getByteMinus128(data[40]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP35:
new_state = PanasonicDecode::getByteMinus128(data[41]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP36:
new_state = PanasonicDecode::getByteMinus128(data[145]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP37:
new_state = PanasonicDecode::getByteMinus128(data[146]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP38:
new_state = PanasonicDecode::getByteMinus1Times200(data[196]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP39:
new_state = PanasonicDecode::getByteMinus1Times200(data[195]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP40:
new_state = PanasonicDecode::getByteMinus1Times200(data[198]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP41:
new_state = PanasonicDecode::getByteMinus1Times200(data[197]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP42:
new_state = PanasonicDecode::getByteMinus128(data[147]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP43:
new_state = PanasonicDecode::getByteMinus128(data[148]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP45:
new_state = PanasonicDecode::getByteMinus128(data[43]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP46:
new_state = PanasonicDecode::getByteMinus128(data[149]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP47:
new_state = PanasonicDecode::getByteMinus128(data[150]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP48:
new_state = PanasonicDecode::getByteMinus128(data[151]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP49:
new_state = PanasonicDecode::getByteMinus128(data[154]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP50:
new_state = PanasonicDecode::getByteMinus128(data[155]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP51:
new_state = PanasonicDecode::getByteMinus128(data[157]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP52:
new_state = PanasonicDecode::getByteMinus128(data[159]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP53:
new_state = PanasonicDecode::getByteMinus128(data[160]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP54:
new_state = PanasonicDecode::getByteMinus128(data[161]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP55:
new_state = PanasonicDecode::getByteMinus128(data[162]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP56:
new_state = PanasonicDecode::getByteMinus128(data[139]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP57:
new_state = PanasonicDecode::getByteMinus128(data[140]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP62:
new_state = PanasonicDecode::getByteMinus1Times10(data[173]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP63:
new_state = PanasonicDecode::getByteMinus1Times10(data[174]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP64:
new_state = PanasonicDecode::getByteMinus1Div5(data[163]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP65:
new_state = PanasonicDecode::getByteMinus1Times50(data[171]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP66:
new_state = PanasonicDecode::getByteMinus1Times50(data[164]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP67:
new_state = PanasonicDecode::getByteMinus1Div5(data[165]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP70:
new_state = PanasonicDecode::getByteMinus128(data[100]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP71:
new_state = PanasonicDecode::getByteMinus1(data[101]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP72:
new_state = PanasonicDecode::getByteMinus128(data[86]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP73:
new_state = PanasonicDecode::getByteMinus128(data[87]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP74:
new_state = PanasonicDecode::getByteMinus128(data[89]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP75:
new_state = PanasonicDecode::getByteMinus128(data[88]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP77:
new_state = PanasonicDecode::getByteMinus128(data[83]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP78:
new_state = PanasonicDecode::getByteMinus128(data[85]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP79:
new_state = PanasonicDecode::getByteMinus128(data[95]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP80:
new_state = PanasonicDecode::getByteMinus128(data[96]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP82:
new_state = PanasonicDecode::getByteMinus128(data[79]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP83:
new_state = PanasonicDecode::getByteMinus128(data[80]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP84:
new_state = PanasonicDecode::getByteMinus128(data[82]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP85:
new_state = PanasonicDecode::getByteMinus128(data[81]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP86:
new_state = PanasonicDecode::getByteMinus128(data[90]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP87:
new_state = PanasonicDecode::getByteMinus128(data[91]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP88:
new_state = PanasonicDecode::getByteMinus128(data[93]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP89:
new_state = PanasonicDecode::getByteMinus128(data[92]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP90:
new_state = PanasonicDecode::getWordMinus1(data, 185);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP91:
new_state = PanasonicDecode::getWordMinus1(data, 188);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP93:
new_state = PanasonicDecode::getByteMinus1(data[172]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP95:
new_state = PanasonicDecode::getByteMinus1(data[45]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP96:
new_state = PanasonicDecode::getByteMinus1(data[104]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP97:
new_state = PanasonicDecode::getByteMinus128(data[105]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP98:
new_state = PanasonicDecode::getByteMinus128(data[106]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP102:
new_state = PanasonicDecode::getByteMinus128(data[61]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP103:
new_state = PanasonicDecode::getByteMinus128(data[62]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP104:
new_state = PanasonicDecode::getByteMinus128(data[63]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP105:
new_state = PanasonicDecode::getByteMinus128(data[64]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP113:
new_state = PanasonicDecode::getByteMinus128(data[59]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP115:
new_state = PanasonicDecode::getByteMinus1Div50(data[125]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP116:
new_state = PanasonicDecode::getByteMinus128(data[126]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP117:
new_state = PanasonicDecode::getByteMinus128(data[127]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP118:
new_state = PanasonicDecode::getByteMinus128(data[128]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP127:
new_state = PanasonicDecode::getByteMinus1Div2(data[177]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP128:
new_state = PanasonicDecode::getByteMinus1Div2(data[178]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP131:
new_state = PanasonicDecode::getByteMinus128(data[65]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP134:
new_state = PanasonicDecode::getByteMinus128(data[66]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP135:
new_state = PanasonicDecode::getByteMinus128(data[68]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP136:
new_state = PanasonicDecode::getByteMinus1(data[67]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP137:
new_state = PanasonicDecode::getByteMinus1(data[69]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_TOP138:
new_state = PanasonicDecode::getByteMinus1(data[70]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP0:
new_state = PanasonicDecode::getWordMinus1(data, 14);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_XTOP1:
new_state = PanasonicDecode::getWordMinus1(data, 16);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_XTOP2:
new_state = PanasonicDecode::getWordMinus1(data, 18);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_XTOP3:
new_state = PanasonicDecode::getWordMinus1(data, 20);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_XTOP4:
new_state = PanasonicDecode::getWordMinus1(data, 22);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case SensorIds::CONF_XTOP5:
new_state = PanasonicDecode::getWordMinus1(data, 24);
if (this->has_state() && this->get_state() == new_state)
return;
break;
default:
return;
};
case SensorIds::CONF_XTOP0:
new_state = PanasonicDecode::getWordMinus1(data, 14);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP1:
new_state = PanasonicDecode::getWordMinus1(data, 16);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP2:
new_state = PanasonicDecode::getWordMinus1(data, 18);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP3:
new_state = PanasonicDecode::getWordMinus1(data, 20);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP4:
new_state = PanasonicDecode::getWordMinus1(data, 22);
if (this->has_state() && this->get_state() == new_state) return;
break;
case SensorIds::CONF_XTOP5:
new_state = PanasonicDecode::getWordMinus1(data, 24);
if (this->has_state() && this->get_state() == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -4,124 +4,121 @@
#include "../panasonic_heatpump.h"
#include "../decode.h"
namespace esphome {
namespace panasonic_heatpump {
enum SensorIds : uint8_t {
CONF_TOP1,
CONF_TOP5,
CONF_TOP6,
CONF_TOP7,
CONF_TOP8,
CONF_TOP9,
CONF_TOP10,
CONF_TOP11,
CONF_TOP12,
CONF_TOP14,
CONF_TOP15,
CONF_TOP16,
CONF_TOP21,
CONF_TOP22,
CONF_TOP23,
CONF_TOP24,
CONF_TOP25,
CONF_TOP27,
CONF_TOP28,
CONF_TOP29,
CONF_TOP30,
CONF_TOP31,
CONF_TOP32,
CONF_TOP33,
CONF_TOP34,
CONF_TOP35,
CONF_TOP36,
CONF_TOP37,
CONF_TOP38,
CONF_TOP39,
CONF_TOP40,
CONF_TOP41,
CONF_TOP42,
CONF_TOP43,
CONF_TOP45,
CONF_TOP46,
CONF_TOP47,
CONF_TOP48,
CONF_TOP49,
CONF_TOP50,
CONF_TOP51,
CONF_TOP52,
CONF_TOP53,
CONF_TOP54,
CONF_TOP55,
CONF_TOP56,
CONF_TOP57,
CONF_TOP62,
CONF_TOP63,
CONF_TOP64,
CONF_TOP65,
CONF_TOP66,
CONF_TOP67,
CONF_TOP70,
CONF_TOP71,
CONF_TOP72,
CONF_TOP73,
CONF_TOP74,
CONF_TOP75,
CONF_TOP77,
CONF_TOP78,
CONF_TOP79,
CONF_TOP80,
CONF_TOP82,
CONF_TOP83,
CONF_TOP84,
CONF_TOP85,
CONF_TOP86,
CONF_TOP87,
CONF_TOP88,
CONF_TOP89,
CONF_TOP90,
CONF_TOP91,
CONF_TOP93,
CONF_TOP95,
CONF_TOP96,
CONF_TOP97,
CONF_TOP98,
CONF_TOP102,
CONF_TOP103,
CONF_TOP104,
CONF_TOP105,
CONF_TOP113,
CONF_TOP115,
CONF_TOP116,
CONF_TOP117,
CONF_TOP118,
CONF_TOP127,
CONF_TOP128,
CONF_TOP131,
CONF_TOP134,
CONF_TOP135,
CONF_TOP136,
CONF_TOP137,
CONF_TOP138,
namespace esphome
{
namespace panasonic_heatpump
{
enum SensorIds : uint8_t
{
CONF_TOP1,
CONF_TOP5,
CONF_TOP6,
CONF_TOP7,
CONF_TOP8,
CONF_TOP9,
CONF_TOP10,
CONF_TOP11,
CONF_TOP12,
CONF_TOP14,
CONF_TOP15,
CONF_TOP16,
CONF_TOP21,
CONF_TOP22,
CONF_TOP23,
CONF_TOP24,
CONF_TOP25,
CONF_TOP27,
CONF_TOP28,
CONF_TOP29,
CONF_TOP30,
CONF_TOP31,
CONF_TOP32,
CONF_TOP33,
CONF_TOP34,
CONF_TOP35,
CONF_TOP36,
CONF_TOP37,
CONF_TOP38,
CONF_TOP39,
CONF_TOP40,
CONF_TOP41,
CONF_TOP42,
CONF_TOP43,
CONF_TOP45,
CONF_TOP46,
CONF_TOP47,
CONF_TOP48,
CONF_TOP49,
CONF_TOP50,
CONF_TOP51,
CONF_TOP52,
CONF_TOP53,
CONF_TOP54,
CONF_TOP55,
CONF_TOP56,
CONF_TOP57,
CONF_TOP62,
CONF_TOP63,
CONF_TOP64,
CONF_TOP65,
CONF_TOP66,
CONF_TOP67,
CONF_TOP70,
CONF_TOP71,
CONF_TOP72,
CONF_TOP73,
CONF_TOP74,
CONF_TOP75,
CONF_TOP77,
CONF_TOP78,
CONF_TOP79,
CONF_TOP80,
CONF_TOP82,
CONF_TOP83,
CONF_TOP84,
CONF_TOP85,
CONF_TOP86,
CONF_TOP87,
CONF_TOP88,
CONF_TOP89,
CONF_TOP90,
CONF_TOP91,
CONF_TOP93,
CONF_TOP95,
CONF_TOP96,
CONF_TOP97,
CONF_TOP98,
CONF_TOP102,
CONF_TOP103,
CONF_TOP104,
CONF_TOP105,
CONF_TOP113,
CONF_TOP115,
CONF_TOP116,
CONF_TOP117,
CONF_TOP118,
CONF_TOP127,
CONF_TOP128,
CONF_TOP131,
CONF_TOP134,
CONF_TOP135,
CONF_TOP136,
CONF_TOP137,
CONF_TOP138,
CONF_XTOP0,
CONF_XTOP1,
CONF_XTOP2,
CONF_XTOP3,
CONF_XTOP4,
CONF_XTOP5,
};
CONF_XTOP0,
CONF_XTOP1,
CONF_XTOP2,
CONF_XTOP3,
CONF_XTOP4,
CONF_XTOP5,
};
class PanasonicHeatpumpSensor : public sensor::Sensor, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpSensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
class PanasonicHeatpumpSensor : public sensor::Sensor,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpSensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,10 +1,11 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import switch
from esphome.const import (
ENTITY_CATEGORY_CONFIG,
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
CONF_SET1 = "set1" # Set Heatpump
CONF_SET10 = "set10" # Set Force DHW
@ -21,77 +22,79 @@ CONF_SET33 = "set33" # Set External Heat Cool Control
CONF_SET34 = "set34" # Set Bivalent Control
TYPES = [
CONF_SET1,
CONF_SET10,
CONF_SET12,
CONF_SET13,
CONF_SET14,
CONF_SET24,
CONF_SET25,
CONF_SET28,
CONF_SET30,
CONF_SET31,
CONF_SET32,
CONF_SET33,
CONF_SET34,
CONF_SET1,
CONF_SET10,
CONF_SET12,
CONF_SET13,
CONF_SET14,
CONF_SET24,
CONF_SET25,
CONF_SET28,
CONF_SET30,
CONF_SET31,
CONF_SET32,
CONF_SET33,
CONF_SET34,
]
PanasonicHeatpumpSwitch = panasonic_heatpump_ns.class_("PanasonicHeatpumpSwitch", switch.Switch, cg.Component)
PanasonicHeatpumpSwitch = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpSwitch", switch.Switch, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_SET1): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET10): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET12): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET13): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET14): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET24): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET25): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET28): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET30): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET31): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET32): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET33): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET34): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_SET1): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET10): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET12): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET13): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET14): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET24): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET25): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET28): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET30): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET31): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET32): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET33): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
cv.Optional(CONF_SET34): switch.switch_schema(
PanasonicHeatpumpSwitch,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await switch.new_switch(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_switch(var))
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await switch.new_switch(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_switch(var))

View File

@ -1,134 +1,140 @@
#include "panasonic_heatpump_switch.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.switch";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.switch";
void PanasonicHeatpumpSwitch::dump_config() {
LOG_SWITCH("", "Panasonic Heatpump Switch", this);
delay(10);
}
void PanasonicHeatpumpSwitch::dump_config()
{
LOG_SWITCH("", "Panasonic Heatpump Switch", this);
delay(10);
}
void PanasonicHeatpumpSwitch::write_state(bool state) {
size_t value = state ? 1 : 0;
void PanasonicHeatpumpSwitch::write_state(bool state)
{
size_t value = state ? 1 : 0;
switch (this->id_) {
case SwitchIds::CONF_SET1:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 4);
break;
case SwitchIds::CONF_SET10:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 4);
break;
case SwitchIds::CONF_SET12:
this->parent_->set_command_byte(PanasonicCommand::setMultiply2(value), 8);
break;
case SwitchIds::CONF_SET13:
this->parent_->set_command_byte(PanasonicCommand::setMultiply4(value), 8);
break;
case SwitchIds::CONF_SET14:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 4);
break;
case SwitchIds::CONF_SET24:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 5);
break;
case SwitchIds::CONF_SET25:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 20);
break;
case SwitchIds::CONF_SET28:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(value), 24);
break;
case SwitchIds::CONF_SET30:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 23);
break;
case SwitchIds::CONF_SET31:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 23);
break;
case SwitchIds::CONF_SET32:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 23);
break;
case SwitchIds::CONF_SET33:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(value), 23);
break;
case SwitchIds::CONF_SET34:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 26);
break;
default:
return;
};
switch (this->id_)
{
case SwitchIds::CONF_SET1:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 4);
break;
case SwitchIds::CONF_SET10:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 4);
break;
case SwitchIds::CONF_SET12:
this->parent_->set_command_byte(PanasonicCommand::setMultiply2(value), 8);
break;
case SwitchIds::CONF_SET13:
this->parent_->set_command_byte(PanasonicCommand::setMultiply4(value), 8);
break;
case SwitchIds::CONF_SET14:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 4);
break;
case SwitchIds::CONF_SET24:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 5);
break;
case SwitchIds::CONF_SET25:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 20);
break;
case SwitchIds::CONF_SET28:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(value), 24);
break;
case SwitchIds::CONF_SET30:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 23);
break;
case SwitchIds::CONF_SET31:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply16(value), 23);
break;
case SwitchIds::CONF_SET32:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply64(value), 23);
break;
case SwitchIds::CONF_SET33:
this->parent_->set_command_byte(PanasonicCommand::setPlus1Multiply4(value), 23);
break;
case SwitchIds::CONF_SET34:
this->parent_->set_command_byte(PanasonicCommand::setPlus1(value), 26);
break;
default: return;
};
this->publish_state(state);
this->keep_state_ = 2;
}
this->publish_state(state);
this->keep_state_ = 2;
}
void PanasonicHeatpumpSwitch::publish_new_state(const std::vector<uint8_t>& data) {
if (this->keep_state_ > 0) {
this->keep_state_--;
return;
}
if (data.empty())
return;
void PanasonicHeatpumpSwitch::publish_new_state(const std::vector<uint8_t>& data)
{
if (this->keep_state_ > 0)
{
this->keep_state_--;
return;
}
if (data.empty()) return;
bool new_state;
switch (this->id_) {
case SwitchIds::CONF_SET1:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[4]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET10:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[4]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET24:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[5]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET12:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[111]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET13:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[117]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET28:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[24]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET25:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[20]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET30:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[23]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET33:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[23]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET31:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[23]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET32:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[23]));
if (this->state == new_state)
return;
break;
case SwitchIds::CONF_SET34:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[26]));
if (this->state == new_state)
return;
break;
default:
return;
};
bool new_state;
switch (this->id_)
{
case SwitchIds::CONF_SET1:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[4]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET10:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[4]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET24:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[5]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET12:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[111]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET13:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[117]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET28:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[24]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET25:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[20]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET30:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[23]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET33:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit5and6(data[23]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET31:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit3and4(data[23]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET32:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit1and2(data[23]));
if (this->state == new_state) return;
break;
case SwitchIds::CONF_SET34:
new_state = PanasonicDecode::getBinaryState(PanasonicDecode::getBit7and8(data[26]));
if (this->state == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -5,38 +5,35 @@
#include "../decode.h"
#include "../commands.h"
namespace esphome {
namespace panasonic_heatpump {
enum SwitchIds : uint8_t {
CONF_SET1,
CONF_SET10,
CONF_SET12,
CONF_SET13,
CONF_SET14,
CONF_SET24,
CONF_SET25,
CONF_SET28,
CONF_SET30,
CONF_SET31,
CONF_SET32,
CONF_SET33,
CONF_SET34,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum SwitchIds : uint8_t
{
CONF_SET1,
CONF_SET10,
CONF_SET12,
CONF_SET13,
CONF_SET14,
CONF_SET24,
CONF_SET25,
CONF_SET28,
CONF_SET30,
CONF_SET31,
CONF_SET32,
CONF_SET33,
CONF_SET34,
};
class PanasonicHeatpumpSwitch : public switch_::Switch,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpSwitch() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
class PanasonicHeatpumpSwitch : public switch_::Switch, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpSwitch() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
protected:
void write_state(bool state) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
protected:
void write_state(bool state) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1,11 +1,11 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
DEVICE_CLASS_HEAT,
from .. import (
CONF_PANASONIC_HEATPUMP_ID,
PanasonicHeatpumpComponent,
panasonic_heatpump_ns,
)
from .. import CONF_PANASONIC_HEATPUMP_ID, PanasonicHeatpumpComponent, panasonic_heatpump_ns
ICON_OPERATION_MODE = "mdi:thermostat"
ICON_POWERFUL_MODE = "mdi:arm-flex"
@ -23,7 +23,6 @@ ICON_VALVE = "mdi:pipe-valve"
ICON_PUMP = "mdi:pump"
ICON_EXTERNAL_PAD_HEATER = "mdi:radiator"
CONF_TOP4 = "top4" # Operation Mode
# ToDo: Split up top4 into top4_1 (Heating Mode State) and top4_2 (DHW Mode State)
CONF_TOP17 = "top17" # Powerful Mode Time
@ -48,126 +47,129 @@ CONF_TOP126 = "top126" # ThreeWay Valve State2
CONF_TOP130 = "top130" # Bivalent Mode
TYPES = [
CONF_TOP4,
CONF_TOP17,
CONF_TOP18,
CONF_TOP19,
CONF_TOP20,
CONF_TOP44,
CONF_TOP58,
CONF_TOP59,
CONF_TOP76,
CONF_TOP81,
CONF_TOP92,
CONF_TOP94,
CONF_TOP101,
CONF_TOP106,
CONF_TOP107,
CONF_TOP111,
CONF_TOP112,
CONF_TOP114,
CONF_TOP125,
CONF_TOP126,
CONF_TOP130,
CONF_TOP4,
CONF_TOP17,
CONF_TOP18,
CONF_TOP19,
CONF_TOP20,
CONF_TOP44,
CONF_TOP58,
CONF_TOP59,
CONF_TOP76,
CONF_TOP81,
CONF_TOP92,
CONF_TOP94,
CONF_TOP101,
CONF_TOP106,
CONF_TOP107,
CONF_TOP111,
CONF_TOP112,
CONF_TOP114,
CONF_TOP125,
CONF_TOP126,
CONF_TOP130,
]
PanasonicHeatpumpTextSensor = panasonic_heatpump_ns.class_("PanasonicHeatpumpTextSensor", text_sensor.TextSensor, cg.Component)
PanasonicHeatpumpTextSensor = panasonic_heatpump_ns.class_(
"PanasonicHeatpumpTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_TOP4): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_OPERATION_MODE,
),
cv.Optional(CONF_TOP17): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_POWERFUL_MODE,
),
cv.Optional(CONF_TOP18): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_QUIET_MODE,
),
cv.Optional(CONF_TOP19): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_HOLIDAY_MODE,
),
cv.Optional(CONF_TOP20): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP44): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_ERROR,
),
cv.Optional(CONF_TOP58): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_BLOCKED,
),
cv.Optional(CONF_TOP59): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_BLOCKED,
),
cv.Optional(CONF_TOP76): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_HEATING_MODE,
),
cv.Optional(CONF_TOP81): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_COOLING_MODE,
),
cv.Optional(CONF_TOP92): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_MODEL,
),
cv.Optional(CONF_TOP94): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_ZONE,
),
cv.Optional(CONF_TOP101): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_SOLAR_MODE,
),
cv.Optional(CONF_TOP106): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_PUMP,
),
cv.Optional(CONF_TOP107): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_LIQUID,
),
cv.Optional(CONF_TOP111): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
),
cv.Optional(CONF_TOP112): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
),
cv.Optional(CONF_TOP114): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_EXTERNAL_PAD_HEATER,
),
cv.Optional(CONF_TOP125): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP126): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP130): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,),
}
{
cv.GenerateID(CONF_PANASONIC_HEATPUMP_ID): cv.use_id(
PanasonicHeatpumpComponent
),
cv.Optional(CONF_TOP4): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_OPERATION_MODE,
),
cv.Optional(CONF_TOP17): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_POWERFUL_MODE,
),
cv.Optional(CONF_TOP18): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_QUIET_MODE,
),
cv.Optional(CONF_TOP19): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_HOLIDAY_MODE,
),
cv.Optional(CONF_TOP20): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP44): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_ERROR,
),
cv.Optional(CONF_TOP58): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_BLOCKED,
),
cv.Optional(CONF_TOP59): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_BLOCKED,
),
cv.Optional(CONF_TOP76): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_HEATING_MODE,
),
cv.Optional(CONF_TOP81): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_COOLING_MODE,
),
cv.Optional(CONF_TOP92): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_MODEL,
),
cv.Optional(CONF_TOP94): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_ZONE,
),
cv.Optional(CONF_TOP101): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_SOLAR_MODE,
),
cv.Optional(CONF_TOP106): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_PUMP,
),
cv.Optional(CONF_TOP107): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_LIQUID,
),
cv.Optional(CONF_TOP111): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
),
cv.Optional(CONF_TOP112): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
),
cv.Optional(CONF_TOP114): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_EXTERNAL_PAD_HEATER,
),
cv.Optional(CONF_TOP125): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP126): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
icon=ICON_VALVE,
),
cv.Optional(CONF_TOP130): text_sensor.text_sensor_schema(
PanasonicHeatpumpTextSensor,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await text_sensor.new_text_sensor(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_text_sensor(var))
parent = await cg.get_variable(config[CONF_PANASONIC_HEATPUMP_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await text_sensor.new_text_sensor(child_config)
await cg.register_component(var, child_config)
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_text_sensor(var))

View File

@ -1,113 +1,137 @@
#include "panasonic_heatpump_text_sensor.h"
namespace esphome {
namespace panasonic_heatpump {
static const char* const TAG = "panasonic_heatpump.text_sensor";
namespace esphome
{
namespace panasonic_heatpump
{
static const char *const TAG = "panasonic_heatpump.text_sensor";
void PanasonicHeatpumpTextSensor::dump_config() {
LOG_TEXT_SENSOR("", "Panasonic Heatpump Text Sensor", this);
delay(10);
}
void PanasonicHeatpumpTextSensor::dump_config()
{
LOG_TEXT_SENSOR("", "Panasonic Heatpump Text Sensor", this);
delay(10);
}
void PanasonicHeatpumpTextSensor::publish_new_state(const std::vector<uint8_t>& data) {
if (data.empty())
return;
void PanasonicHeatpumpTextSensor::publish_new_state(const std::vector<uint8_t>& data)
{
if (data.empty()) return;
std::string new_state;
switch (this->id_) {
case TextSensorIds::CONF_TOP4:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::OperationMode, PanasonicDecode::getOperationMode(data[6]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP17:
new_state = PanasonicDecode::getTextState(PanasonicDecode::PowerfulMode, PanasonicDecode::getBit6and7and8(data[7]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP18:
new_state = PanasonicDecode::getTextState(PanasonicDecode::QuietMode, PanasonicDecode::getBit3and4and5(data[7]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP19:
new_state = PanasonicDecode::getTextState(PanasonicDecode::HolidayState, PanasonicDecode::getBit3and4(data[5]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP20:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ThreeWayValve, PanasonicDecode::getBit7and8(data[111]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP44:
new_state = PanasonicDecode::getErrorInfo(data[113], data[114]);
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP58:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BlockedFree, PanasonicDecode::getBit5and6(data[9]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP59:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BlockedFree, PanasonicDecode::getBit7and8(data[9]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP76:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::WaterTempControl, PanasonicDecode::getBit7and8(data[28]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP81:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::WaterTempControl, PanasonicDecode::getBit5and6(data[28]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP92:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ModelNames, PanasonicDecode::getModel(data, 129));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP94:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneState, PanasonicDecode::getBit1and2(data[6]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP101:
new_state = PanasonicDecode::getTextState(PanasonicDecode::SolarMode, PanasonicDecode::getBit3and4(data[24]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP106:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::PumpFlowRateMode, PanasonicDecode::getBit3and4(data[29]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP107:
new_state = PanasonicDecode::getTextState(PanasonicDecode::LiquidType, PanasonicDecode::getBit1(data[20]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP111:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::ZoneSensorType, PanasonicDecode::getLowNibbleMinus1(data[22]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP112:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::ZoneSensorType, PanasonicDecode::getHighNibbleMinus1(data[22]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP114:
new_state =
PanasonicDecode::getTextState(PanasonicDecode::ExtPadHeaterType, PanasonicDecode::getBit3and4(data[25]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP125:
new_state = PanasonicDecode::getTextState(PanasonicDecode::TwoWayValve, PanasonicDecode::getBit5and6(data[116]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP126:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ThreeWayValve, PanasonicDecode::getBit7and8(data[116]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
case TextSensorIds::CONF_TOP130:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BivalentMode, PanasonicDecode::getBit5and6(data[26]));
if (this->has_state() && this->get_state() == new_state)
return;
break;
default:
return;
};
std::string new_state;
switch (this->id_)
{
case TextSensorIds::CONF_TOP4:
new_state = PanasonicDecode::getTextState(PanasonicDecode::OperationMode, PanasonicDecode::getOperationMode(data[6]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP17:
new_state = PanasonicDecode::getTextState(PanasonicDecode::PowerfulMode, PanasonicDecode::getBit6and7and8(data[7]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP18:
new_state = PanasonicDecode::getTextState(PanasonicDecode::QuietMode, PanasonicDecode::getBit3and4and5(data[7]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP19:
new_state = PanasonicDecode::getTextState(PanasonicDecode::HolidayState, PanasonicDecode::getBit3and4(data[5]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP20:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ThreeWayValve, PanasonicDecode::getBit7and8(data[111]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP44:
new_state = PanasonicDecode::getErrorInfo(data[113], data[114]);
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP58:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BlockedFree, PanasonicDecode::getBit5and6(data[9]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP59:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BlockedFree, PanasonicDecode::getBit7and8(data[9]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP76:
new_state = PanasonicDecode::getTextState(PanasonicDecode::WaterTempControl, PanasonicDecode::getBit7and8(data[28]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP81:
new_state = PanasonicDecode::getTextState(PanasonicDecode::WaterTempControl, PanasonicDecode::getBit5and6(data[28]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP92:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ModelNames, PanasonicDecode::getModel(data, 129));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP94:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneState, PanasonicDecode::getBit1and2(data[6]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP101:
new_state = PanasonicDecode::getTextState(PanasonicDecode::SolarMode, PanasonicDecode::getBit3and4(data[24]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP106:
new_state = PanasonicDecode::getTextState(PanasonicDecode::PumpFlowRateMode, PanasonicDecode::getBit3and4(data[29]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP107:
new_state = PanasonicDecode::getTextState(PanasonicDecode::LiquidType, PanasonicDecode::getBit1(data[20]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP111:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneSensorType, PanasonicDecode::getLowNibbleMinus1(data[22]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP112:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ZoneSensorType, PanasonicDecode::getHighNibbleMinus1(data[22]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP114:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ExtPadHeaterType, PanasonicDecode::getBit3and4(data[25]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP125:
new_state = PanasonicDecode::getTextState(PanasonicDecode::TwoWayValve, PanasonicDecode::getBit5and6(data[116]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP126:
new_state = PanasonicDecode::getTextState(PanasonicDecode::ThreeWayValve, PanasonicDecode::getBit7and8(data[116]));
if (this->has_state() && this->get_state() == new_state) return;
break;
case TextSensorIds::CONF_TOP130:
new_state = PanasonicDecode::getTextState(PanasonicDecode::BivalentMode, PanasonicDecode::getBit5and6(data[26]));
if (this->has_state() && this->get_state() == new_state) return;
break;
default: return;
};
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome
this->publish_state(new_state);
}
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -4,43 +4,40 @@
#include "../panasonic_heatpump.h"
#include "../decode.h"
namespace esphome {
namespace panasonic_heatpump {
enum TextSensorIds : uint8_t {
CONF_TOP4,
CONF_TOP17,
CONF_TOP18,
CONF_TOP19,
CONF_TOP20,
CONF_TOP44,
CONF_TOP58,
CONF_TOP59,
CONF_TOP76,
CONF_TOP81,
CONF_TOP92,
CONF_TOP94,
CONF_TOP101,
CONF_TOP106,
CONF_TOP107,
CONF_TOP111,
CONF_TOP112,
CONF_TOP114,
CONF_TOP125,
CONF_TOP126,
CONF_TOP130,
};
namespace esphome
{
namespace panasonic_heatpump
{
enum TextSensorIds : uint8_t
{
CONF_TOP4,
CONF_TOP17,
CONF_TOP18,
CONF_TOP19,
CONF_TOP20,
CONF_TOP44,
CONF_TOP58,
CONF_TOP59,
CONF_TOP76,
CONF_TOP81,
CONF_TOP92,
CONF_TOP94,
CONF_TOP101,
CONF_TOP106,
CONF_TOP107,
CONF_TOP111,
CONF_TOP112,
CONF_TOP114,
CONF_TOP125,
CONF_TOP126,
CONF_TOP130,
};
class PanasonicHeatpumpTextSensor : public text_sensor::TextSensor, public Component,
public Parented<PanasonicHeatpumpComponent>, public PanasonicHeatpumpEntity
{
public:
PanasonicHeatpumpTextSensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome
class PanasonicHeatpumpTextSensor : public text_sensor::TextSensor,
public Component,
public Parented<PanasonicHeatpumpComponent>,
public PanasonicHeatpumpEntity {
public:
PanasonicHeatpumpTextSensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace panasonic_heatpump
} // namespace esphome

View File

@ -1 +1,128 @@
prototypes/maidesite_desk/example_basic.yaml
esp32:
board: esp32-c3-devkitm-1
variant: ESP32C3
framework:
type: esp-idf
esphome:
name: maidesite-desk-controller
friendly_name: Maidesite Desk Controller
ota:
platform: esphome
password: !secret ota_password
api:
encryption:
key: !secret encryption_key
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Maidesite Desk Fallback"
password: !secret fallback_password
logger:
external_components:
- source:
type: git
url: https://github.com/ElVit/esphome_components/
components: [ maidesite_desk ]
uart:
- id: uart_bus
tx_pin: GPIO03
rx_pin: GPIO04
baud_rate: 9600
maidesite_desk_controller:
id: my_desk
uart_id: uart_bus
log_uart_msg: true
sensor:
- platform: maidesite_desk
height_abs:
name: "Height absolut"
height_pct:
name: "Height percent"
height_min:
name: "Height Min"
height_max:
name: "Height Max"
position_m1:
name: "Position M1"
position_m2:
name: "Position M2"
position_m3:
name: "Position M3"
position_m4:
name: "Position M4"
button:
- platform: maidesite_desk
stop:
name: "Stop"
step_up:
name: "Step up"
step_down:
name: "Step down"
goto_max:
name: "Move to MAX"
goto_min:
name: "Move to MIN"
goto_m1:
name: "Move to M1"
goto_m2:
name: "Move to M2"
goto_m3:
name: "Move to M3"
goto_m4:
name: "Move to M4"
save_m1:
name: "Save M1"
save_m2:
name: "Save M2"
save_m3:
name: "Save M3"
save_m4:
name: "Save M4"
number:
- platform: maidesite_desk
height_abs:
name: "Height absolut"
mode: SLIDER
height_pct:
name: "Height percent"
mode: SLIDER
## lambda usage
# button:
# - platform: template
# name: "Step up"
# on_press:
# lambda: "id(my_desk).step_up();"
# - platform: template
# name: "Step down"
# on_press:
# lambda: "id(my_desk).step_down();"
# - platform: template
# name: "Stop"
# on_press:
# lambda: "id(my_desk).stop();"
# - platform: template
# name: "Position 2"
# on_press:
# lambda: "id(my_desk).goto_mem_position(2);"
# - platform: template
# name: "Save Position 4"
# on_press:
# lambda: "id(my_desk).save_mem_position(4);"
# - platform: template
# name: "Go to 100cm"
# on_press:
# lambda: "id(my_desk).goto_height(100);"

View File

@ -1,12 +1,3 @@
substitutions:
mdns_name: panasonic-heatpump
device_name: Panasonic Heatpump
pin_rx_hp: GPIO37 # heatpump reads data (RX) on this pin (yellow)
pin_tx_hp: GPIO39 # heatpump sends data (TX) on this pin (green)
pin_tx_wm: GPIO18 # WiFi module sends data (TX) on this pin (white)
pin_rx_wm: GPIO16 # WiFi module reads data (RX) on this pin (blue)
esp32:
board: lolin_s2_mini
variant: ESP32S2
@ -14,13 +5,12 @@ esp32:
type: esp-idf
esphome:
name: $mdns_name
friendly_name: $device_name
name: panasonic-heatpump
friendly_name: Panasonic Heatpump
ota:
platform: esphome
password: !secret ota_password
id: ota_pass
api:
encryption:
@ -30,43 +20,33 @@ wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "${device_name} Fallback"
ssid: "Panasonic Heatpump Fallback"
password: !secret fallback_password
web_server:
port: 80
auth:
username: !secret webserver_username
password: !secret webserver_password
captive_portal:
improv_serial:
logger:
uart:
- id: uart_heatpump
tx_pin: $pin_rx_hp
rx_pin: $pin_tx_hp
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
- id: uart_cz_taw1
tx_pin: $pin_rx_wm
rx_pin: $pin_tx_wm
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
external_components:
- source:
type: git
url: https://github.com/ElVit/esphome_components/
components: [ panasonic_heatpump ]
uart:
- id: uart_heatpump
tx_pin: GPIO37 # heatpump reads data (RX) on this pin (yellow)
rx_pin: GPIO39 # heatpump sends data (TX) on this pin (green)
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
- id: uart_cz_taw1
tx_pin: GPIO16 # WiFi module reads data (RX) on this pin (blue)
rx_pin: GPIO18 # WiFi module sends data (TX) on this pin (white)
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
@ -419,23 +399,23 @@ number:
name: "Set DHW Temp"
set15:
name: "Set Max Pump Duty"
set16_1:
set16_01:
name: "Set Zone1 Heat Target High"
set16_2:
set16_02:
name: "Set Zone1 Heat Target Low"
set16_3:
set16_03:
name: "Set Zone1 Heat Outside Low"
set16_4:
set16_04:
name: "Set Zone1 Heat Outside High"
set16_5:
set16_05:
name: "Set Zone2 Heat Target High"
set16_6:
set16_06:
name: "Set Zone2 Heat Target Low"
set16_7:
set16_07:
name: "Set Zone2 Heat Outside Low"
set16_8:
set16_08:
name: "Set Zone2 Heat Outside High"
set16_9:
set16_09:
name: "Set Zone1 Cool Target High"
set16_10:
name: "Set Zone1 Cool Target Low"
@ -474,8 +454,38 @@ number:
set38:
name: "Set Bivalent AP Stop Temp"
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
set10:
name: "Set Force DHW"
set12:
name: "Set Force Defrost"
set13:
name: "Set Force Sterilization"
set14:
name: "Set Pump"
set24:
name: "Set Main Schedule"
set25:
name: "Set Alt External Sensor"
set28:
name: "Set Buffer"
set30:
name: "Set External Control"
set31:
name: "Set External Error"
set32:
name: "Set External Compressor Control"
set33:
name: "Set External Heat Cool Control"
set34:
name: "Set Bivalent Control"
select:
- platform: panasonic_heatpump
cool_mode: true
set2:
name: "Set Holiday Mode"
set3:
@ -491,31 +501,12 @@ select:
set35:
name: "Set Bivalent Mode"
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
set10:
name: "Set Force DHW"
set12:
name: "Set Force Defrost"
set13:
name: "Set Force Sterilization"
set14:
name: "Set Pump"
set24:
name: "Set Main Schedule"
set25:
name: "Set Alt External Sensor"
set28:
name: "Set Buffer"
set30:
name: "Set External Control"
set31:
name: "Set External Error"
set32:
name: "Set External Compressor Control"
set33:
name: "Set External Heat Cool Control"
set34:
name: "Set Bivalent Control"
climate:
- platform: panasonic_heatpump
cool_mode: true
tank:
name: "DHW"
zone1:
name: "Zone 1"
zone2:
name: "Zone 2"

View File

@ -1,70 +0,0 @@
esphome:
name: maidesite-desk
friendly_name: Maidesite Desk
external_components:
- source:
type: git
url: https://github.com/ElVit/esphome_components/
components: [ maidesite_desk ]
uart:
- id: uart_bus
tx_pin: $pin_tx
rx_pin: $pin_rx
baud_rate: 9600
maidesite_desk_controller:
id: my_desk
sensor:
- platform: maidesite_desk
height_abs:
name: "Height"
button:
- platform: maidesite_desk
stop:
name: "Stop"
step_up:
name: "Step up"
step_down:
name: "Step down"
goto_max:
name: "Move to MAX"
goto_min:
name: "Move to MIN"
goto_m1:
name: "Move to M1"
goto_m2:
name: "Move to M2"
goto_m3:
name: "Move to M3"
goto_m4:
name: "Move to M4"
number:
- platform: maidesite_desk
height_abs:
name: "Height"
mode: SLIDER
# the usual stuff
esp8266:
board: esp01_1m
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "esphome-desk"
password: "9ebc6eac0b4e0e26b8d3b955ec660557"
captive_portal:
api:
encryption:
key: !secret encryption_key
ota:
logger:

View File

@ -1,128 +0,0 @@
esphome:
name: maidesite-desk-controller
friendly_name: Maidesite Desk Controller
external_components:
- source:
type: local
path: esphome_components/components/
# type: git
# url: https://github.com/ElVit/esphome_components/
components: [ maidesite_desk ]
uart:
- id: uart_bus
tx_pin: $pin_tx
rx_pin: $pin_rx
baud_rate: 9600
maidesite_desk_controller:
id: my_desk
uart_id: uart_bus
sensor:
- platform: maidesite_desk
height_abs:
name: "Height absolut"
height_pct:
name: "Height percent"
height_min:
name: "Height Min"
height_max:
name: "Height Max"
position_m1:
name: "Position M1"
position_m2:
name: "Position M2"
position_m3:
name: "Position M3"
position_m4:
name: "Position M4"
button:
- platform: maidesite_desk
stop:
name: "Stop"
step_up:
name: "Step up"
step_down:
name: "Step down"
goto_max:
name: "Move to MAX"
goto_min:
name: "Move to MIN"
goto_m1:
name: "Move to M1"
goto_m2:
name: "Move to M2"
goto_m3:
name: "Move to M3"
goto_m4:
name: "Move to M4"
save_m1:
name: "Save M1"
save_m2:
name: "Save M2"
save_m3:
name: "Save M3"
save_m4:
name: "Save M4"
number:
- platform: maidesite_desk
height_abs:
name: "Height absolut"
mode: SLIDER
height_pct:
name: "Height percent"
mode: SLIDER
## lambda usage
# button:
# - platform: template
# name: "Step up"
# on_press:
# lambda: "id(my_desk).step_up();"
# - platform: template
# name: "Step down"
# on_press:
# lambda: "id(my_desk).step_down();"
# - platform: template
# name: "Stop"
# on_press:
# lambda: "id(my_desk).stop();"
# - platform: template
# name: "Position 2"
# on_press:
# lambda: "id(my_desk).goto_mem_position(2);"
# - platform: template
# name: "Save Position 4"
# on_press:
# lambda: "id(my_desk).save_mem_position(4);"
# - platform: template
# name: "Go to 100cm"
# on_press:
# lambda: "id(my_desk).goto_height(100);"
# the usual stuff
esp32:
board: esp32-c3-devkitm-1
variant: ESP32C3
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "esphome-desk"
password: "9ebc6eac0b4e0e26b8d3b955ec660557"
captive_portal:
api:
encryption:
key: !secret encryption_key
ota:
logger:

45
pytest.ini Normal file
View File

@ -0,0 +1,45 @@
[tool:pytest]
# Pytest configuration for all component tests
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# Coverage settings
addopts =
--verbose
--strict-markers
--tb=short
--cov-report=term-missing
--cov-report=html
--cov-report=xml
# Markers
markers =
unit: Unit tests that don't require ESPHome compilation
integration: Integration tests that compile ESPHome configurations
slow: Tests that take a long time to run
# Timeout for tests (in seconds)
timeout = 300
# Coverage options
[coverage:run]
source = components/panasonic_heatpump
omit =
*/tests/*
*/test_*
[coverage:report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
if TYPE_CHECKING:
@abstractmethod
[coverage:html]
directory = htmlcov

88
tests/README.md Normal file
View File

@ -0,0 +1,88 @@
# ESPHome Components Tests
This directory contains tests for all custom ESPHome components in this repository.
## Directory Structure
```
tests/
├── requirements.txt # Common Python dependencies for all tests
├── panasonic_heatpump/ # Tests for panasonic_heatpump component
│ ├── README.md
│ ├── test_panasonic_heatpump_unit.py # Unit tests
│ ├── test_panasonic_heatpump_integration.py # Integration tests
│ ├── test_panasonic_heatpump_esp8266.yaml # ESP8266 test config
│ ├── test_panasonic_heatpump_esp32s2.yaml # ESP32-S2 test config
│ ├── test_panasonic_heatpump_esp32c3.yaml # ESP32-C3 test config
│ ├── test_panasonic_heatpump_cztaw1.yaml # CZ-TAW1 test config
│ ├── test_panasonic_heatpump_mini.yaml # Minimal ESP32 test config
│ └── test_panasonic_heatpump_full.yaml # Full ESP32 test config
└── [other_component]/ # Tests for other components (future)
```
## Running Tests
### Install Dependencies
```bash
pip install -r tests/requirements.txt
```
### Run All Tests
```bash
pytest tests/ -v
```
### Run Tests for Specific Component
```bash
# Panasonic Heatpump
pytest tests/panasonic_heatpump/ -v
# Other components (when added)
pytest tests/[component_name]/ -v
```
### Run with Coverage
```bash
pytest tests/ -v --cov=components --cov-report=html
```
## Adding Tests for New Components
When adding a new component, create a subdirectory under `tests/` with the same name as your component:
1. Create directory: `tests/[component_name]/`
2. Add test files:
- `test_[component_name].yaml` - ESPHome configuration for testing
- `test_[component_name]_unit.py` - Python unit tests
- `test_[component_name]_integration.py` - Integration tests
- `README.md` - Component-specific test documentation
3. Create GitHub Actions workflow: `.github/workflows/test_[component_name].yml`
## Test Types
### Unit Tests
- Test Python configuration validation
- Test component metadata
- Test schema structure
- Fast execution, no compilation required
### Integration Tests
- Test ESPHome configuration validation
- Test actual firmware compilation
- Test various configuration scenarios
- Slower execution, requires ESPHome
## Configuration
Test configuration is managed in `pytest.ini` at the repository root.
## GitHub Actions
Each component should have its own GitHub Actions workflow in `.github/workflows/` that:
- Validates ESPHome configurations
- Compiles test firmware
- Runs Python unit tests
- Checks code formatting
- Reports coverage
See `panasonic_heatpump` for a complete example.

View File

@ -0,0 +1,208 @@
# Testing Documentation for Panasonic Heatpump Component
This directory contains comprehensive tests for the `panasonic_heatpump` ESPHome component.
## Test Structure
### Unit Tests (`test_panasonic_heatpump_unit.py`)
- Tests Python configuration validation
- Tests component metadata (CODEOWNERS, DEPENDENCIES, etc.)
- Tests configuration schema structure
- Tests all platform imports (sensor, binary_sensor, text_sensor, number, select, switch, climate)
- Tests code generation logic
### Integration Tests (`test_panasonic_heatpump_integration.py`)
- Tests minimal feature configuration on ESP32 board
- Tests full feature configuration on ESP32 board
- Tests configuration on ESP8266 board
- Tests configuration on ESP32-S2 board
- Tests configuration on ESP32-C3 board
- Tests configuration with CZ-TAW1 (UART-proxy)
### Test Configuration Files
- `test_panasonic_heatpump_esp8266.yaml` - ESP8266 Wemos D1 Mini
- `test_panasonic_heatpump_esp32s2.yaml` - ESP32-S2 Wemos S2 Mini
- `test_panasonic_heatpump_esp32c3.yaml` - ESP32-C3 mini (RISC-V)
- `test_panasonic_heatpump_cztaw1.yaml` - UART proxy setup for CZ-TAW1 client support
- `test_panasonic_heatpump_mini.yaml` - Minimal ESP32 configuration for basic testing
- `test_panasonic_heatpump_full.yaml` - Comprehensive ESP32 configuration with all features
## Running Tests Locally
### Prerequisites
```bash
pip install -r tests/requirements.txt
```
### Run All Tests
```bash
pytest tests/panasonic_heatpump/ -v
```
### Run Only Unit Tests
```bash
pytest tests/panasonic_heatpump/test_panasonic_heatpump_unit.py -v
```
### Run Only Integration Tests
```bash
pytest tests/panasonic_heatpump/test_panasonic_heatpump_integration.py -v
```
### Run with Coverage
```bash
pytest tests/panasonic_heatpump/ -v --cov=components/panasonic_heatpump --cov-report=html
```
View coverage report by opening `htmlcov/index.html` in a browser.
### Run Specific Test
```bash
pytest tests/panasonic_heatpump/test_panasonic_heatpump_unit.py::TestPanasonicHeatpumpConfig::test_component_metadata -v
```
## GitHub Actions Workflow
The GitHub Actions workflow (`.github/workflows/test_panasonic_heatpump.yml`) includes:
### Jobs
1. **test-build**: Validates and compiles the component
- Tests on multiple board types: ESP8266, ESP32 (full), ESP32-S2, ESP32-C3 and with CZ-TAW1 (UART-proxy)
- Uses esphome/build-action for firmware compilation
2. **lint-code**: Runs clang-format on C++ code
- Ensures consistent code formatting
- Checks all .cpp and .h files
3. **lint-python**: Runs Python linters
- black: Code formatting
- flake8: Style guide enforcement
4. **test-python**: Runs Python unit tests
- Executes pytest with coverage
- Uploads coverage to Codecov
5. **test-minimal-config**: Tests minimal configuration
- Validates minimal configuration file
- Ensures component works with bare minimum settings
6. **test-full-config**: Tests full configuration
- Validates full feature configuration with all platforms
7. **summary**: Aggregates all test results
- Fails if any test job fails
- Provides overview of all test results
### Triggers
The workflow runs on:
- Push to `main` or `heatpump**` branches
- Pull requests to `main` branch
- Manual workflow dispatch
- When relevant files change:
- `components/panasonic_heatpump/**`
- `tests/panasonic_heatpump/**`
- `.github/workflows/test_panasonic_heatpump.yml`
## Test Coverage
The test suite covers:
✅ Component configuration validation
✅ UART dependency checking
✅ All sensor platforms
✅ All binary sensor platforms
✅ All text sensor platforms
✅ All number control platforms
✅ All select control platforms
✅ All switch control platforms
✅ All climate control platforms
✅ Code compilation for different ESP boards
✅ C++ code formatting
✅ Python code formatting and style
## Configuration Examples
### Minimal Configuration
```yaml
uart:
- id: uart_hp
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 9600
parity: EVEN
panasonic_heatpump:
id: hp
```
### Full Configuration
```yaml
uart:
- id: uart_heatpump
tx_pin: GPIO37
rx_pin: GPIO39
baud_rate: 9600
parity: EVEN
- id: uart_client
tx_pin: GPIO18
rx_pin: GPIO16
baud_rate: 9600
parity: EVEN
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
uart_client_id: uart_client
log_uart_msg: true
update_interval: 3s
```
## Adding New Tests
When adding new features to the component:
1. Update `test_panasonic_heatpump.yaml` with new configuration options
2. Add unit tests in `test_panasonic_heatpump_unit.py`
3. Add integration tests in `test_panasonic_heatpump_integration.py` if needed
4. Update this README with new test coverage information
## Troubleshooting
### ESPHome Not Found
If you get "ESPHome not installed" errors, install it:
```bash
pip install esphome
```
### Import Errors
Make sure you're running pytest from the repository root:
```bash
cd /path/to/esphome_components
pytest tests/ -v
```
### Timeout Errors
Some tests may timeout on slower systems. Increase the timeout in `pytest.ini`:
```ini
timeout = 600 # 10 minutes
```
## Continuous Integration
All tests must pass before merging to main branch. The GitHub Actions workflow ensures:
- Code compiles successfully
- Code follows formatting standards
- All unit and integration tests pass
- Latest ESPHome versions is supported
- ESP8266 and ESP32 boards are supported
## Contributing
When contributing to the panasonic_heatpump component:
1. Write tests for new features
2. Ensure all existing tests pass
3. Maintain or improve code coverage
4. Follow the existing code style
5. Update this documentation as needed

View File

@ -0,0 +1,87 @@
---
# Test configuration for panasonic_heatpump component with CZ-TAW1 (UART-man-in-the-middle)
# Tests the proxy functionality of the CZ-TAW1 client to the heatpump
esp32:
board: esp32dev
framework:
type: esp-idf
esphome:
name: test-panasonic-heatpump-cztaw1
friendly_name: "Test Panasonic Heatpump CZ-TAW1"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
- id: uart_cz_taw1
tx_pin: GPIO19
rx_pin: GPIO18
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test configuration with cz_taw1 client
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
uart_client_id: uart_cz_taw1
log_uart_msg: true
update_interval: 3s
# Test sensor types - first item only
sensor:
- platform: panasonic_heatpump
top1:
name: "Pump Flow"
# Test binary sensors - first item only
binary_sensor:
- platform: panasonic_heatpump
top0:
name: "Heatpump State"
# Test text sensors - first item only
text_sensor:
- platform: panasonic_heatpump
top4:
name: "Operating Mode State"
# Test number controls - first item only
number:
- platform: panasonic_heatpump
set5:
name: "Set Z1 Heat Request Temperature"
# Test select controls - first item only
select:
- platform: panasonic_heatpump
set2:
name: "Set Holiday Mode"
# Test switch controls - first item only
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
# Test climate controls - first item only
climate:
- platform: panasonic_heatpump
tank:
name: "DHW"

View File

@ -0,0 +1,78 @@
---
# Test configuration for panasonic_heatpump component on ESP32-C3
# ESP32-C3 is a compact chip with limited GPIO pins
esp32:
board: esp32-c3-devkitm-1
variant: esp32c3
framework:
type: esp-idf
esphome:
name: test-panasonic-heatpump-esp32c3
friendly_name: "Test Panasonic Heatpump ESP32-C3"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO4
rx_pin: GPIO5
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test basic configuration
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
# Test sensor types - first item only
sensor:
- platform: panasonic_heatpump
top1:
name: "Pump Flow"
# Test binary sensors - first item only
binary_sensor:
- platform: panasonic_heatpump
top0:
name: "Heatpump State"
# Test text sensors - first item only
text_sensor:
- platform: panasonic_heatpump
top4:
name: "Operating Mode State"
# Test number controls - first item only
number:
- platform: panasonic_heatpump
set5:
name: "Set Z1 Heat Request Temperature"
# Test select controls - first item only
select:
- platform: panasonic_heatpump
set2:
name: "Set Holiday Mode"
# Test switch controls - first item only
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
# Test climate controls - first item only
climate:
- platform: panasonic_heatpump
tank:
name: "DHW"

View File

@ -0,0 +1,78 @@
---
# Test configuration for panasonic_heatpump component on ESP32-S2
# ESP32-S2 has GPIO pins but limited compared to ESP32
esp32:
board: lolin_s2_mini
variant: esp32s2
framework:
type: esp-idf
esphome:
name: test-panasonic-heatpump-esp32s2
friendly_name: "Test Panasonic Heatpump ESP32-S2"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO39
rx_pin: GPIO37
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test basic configuration
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
# Test sensor types - first item only
sensor:
- platform: panasonic_heatpump
top1:
name: "Pump Flow"
# Test binary sensors - first item only
binary_sensor:
- platform: panasonic_heatpump
top0:
name: "Heatpump State"
# Test text sensors - first item only
text_sensor:
- platform: panasonic_heatpump
top4:
name: "Operating Mode State"
# Test number controls - first item only
number:
- platform: panasonic_heatpump
set5:
name: "Set Z1 Heat Request Temperature"
# Test select controls - first item only
select:
- platform: panasonic_heatpump
set2:
name: "Set Holiday Mode"
# Test switch controls - first item only
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
# Test climate controls - first item only
climate:
- platform: panasonic_heatpump
tank:
name: "DHW"

View File

@ -0,0 +1,75 @@
---
# Test configuration for panasonic_heatpump component on ESP8266
# ESP8266 has limited GPIO pins and only one UART
esp8266:
board: d1_mini
esphome:
name: test-panasonic-heatpump-esp8266
friendly_name: "Test Panasonic Heatpump ESP8266"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO13
rx_pin: GPIO15
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test basic configuration
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
# Test sensor types - first item only
sensor:
- platform: panasonic_heatpump
top1:
name: "Pump Flow"
# Test binary sensors - first item only
binary_sensor:
- platform: panasonic_heatpump
top0:
name: "Heatpump State"
# Test text sensors - first item only
text_sensor:
- platform: panasonic_heatpump
top4:
name: "Operating Mode State"
# Test number controls - first item only
number:
- platform: panasonic_heatpump
set5:
name: "Set Z1 Heat Request Temperature"
# Test select controls - first item only
select:
- platform: panasonic_heatpump
set2:
name: "Set Holiday Mode"
# Test switch controls - first item only
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
# Test climate controls - first item only
climate:
- platform: panasonic_heatpump
tank:
name: "DHW"

View File

@ -0,0 +1,480 @@
---
# Test configuration for panasonic_heatpump component
# This file validates all component features and configurations
esp32:
board: esp32dev
framework:
type: esp-idf
esphome:
name: test-panasonic-heatpump-full
friendly_name: "Test Panasonic Heatpump Full"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
- id: uart_cz_taw1
tx_pin: GPIO19
rx_pin: GPIO18
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test basic configuration
panasonic_heatpump:
id: my_heatpump
uart_id: uart_heatpump
uart_client_id: uart_cz_taw1
log_uart_msg: true
update_interval: 3s
# Test all sensor types
sensor:
- platform: panasonic_heatpump
top1:
name: "Pump Flow"
top5:
name: "Main Inlet Temp"
top6:
name: "Main Outlet Temp"
top7:
name: "Main Target Temp"
top8:
name: "Compressor Freq"
top9:
name: "DHW Target Temp"
top10:
name: "DHW Temp"
top11:
name: "Operations Hours"
top12:
name: "Operations Counter"
top14:
name: "Outside Temp"
top15:
name: "Heat Power Production"
top16:
name: "Heat Power Consumption"
id: heat_power_cons
top21:
name: "Outside Pipe Temp"
top22:
name: "DHW Heat Delta"
top23:
name: "Heat Delta"
top24:
name: "Cool Delta"
top25:
name: "DHW Holiday Shift Temp"
top27:
name: "Z1 Heat Request Temp"
top28:
name: "Z1 Cool Request Temp"
top29:
name: "Z1 Heat Curve Target High Temp"
top30:
name: "Z1 Heat Curve Target Low Temp"
top31:
name: "Z1 Heat Curve Outside High Temp"
top32:
name: "Z1 Heat Curve Outside Low Temp"
top33:
name: "Room Thermostat Temp"
top34:
name: "Z2 Heat Request Temp"
top35:
name: "Z2 Cool Request Temp"
top36:
name: "Z1 Water Temp"
top37:
name: "Z2 Water Temp"
top38:
name: "Cool Power Production"
top39:
name: "Cool Power Consumption"
id: cool_power_cons
top40:
name: "DHW Power Production"
top41:
name: "DHW Power Consumption"
id: dhw_power_cons
top42:
name: "Z1 Water Target Temp"
top43:
name: "Z2 Water Target Temp"
top45:
name: "Room Holiday Shift Temp"
top46:
name: "Buffer Temp"
top47:
name: "Solar Temp"
top48:
name: "Pool Temp"
top49:
name: "Main Hex Outlet Temp"
top50:
name: "Discharge Temp"
top51:
name: "Inside Pipe Temp"
top52:
name: "Defrost Temp"
top53:
name: "Eva Outlet Temp"
top54:
name: "Bypass Outlet Temp"
top55:
name: "Ipm Temp"
top56:
name: "Z1 Temp"
top57:
name: "Z2 Temp"
top62:
name: "Fan1 Motor Speed"
top63:
name: "Fan2 Motor Speed"
top64:
name: "High Pressure"
top65:
name: "Pump Speed"
top66:
name: "Low Pressure"
top67:
name: "Compressor Current"
top70:
name: "Sterilization Temp"
top71:
name: "Sterilization Max Time"
top72:
name: "Z1 Cool Curve Target High Temp"
top73:
name: "Z1 Cool Curve Target Low Temp"
top74:
name: "Z1 Cool Curve Outside High Temp"
top75:
name: "Z1 Cool Curve Outside Low Temp"
top77:
name: "Heating Off Outdoor Temp"
top78:
name: "Heater On Outdoor Temp"
top79:
name: "Heat To Cool Temp"
top80:
name: "Cool To Heat Temp"
top82:
name: "Z2 Heat Curve Target High Temp"
top83:
name: "Z2 Heat Curve Target Low Temp"
top84:
name: "Z2 Heat Curve Outside High Temp"
top85:
name: "Z2 Heat Curve Outside Low Temp"
top86:
name: "Z2 Cool Curve Target High Temp"
top87:
name: "Z2 Cool Curve Target Low Temp"
top88:
name: "Z2 Cool Curve Outside High Temp"
top89:
name: "Z2 Cool Curve Outside Low Temp"
top90:
name: "Room Heater Operations Hours"
top91:
name: "DHW Heater Operations Hours"
top93:
name: "Pump Duty"
top95:
name: "Max Pump Duty"
top96:
name: "Heater Delay Time"
top97:
name: "Heater Start Delta"
top98:
name: "Heater Stop Delta"
top102:
name: "Solar On Delta"
top103:
name: "Solar Off Delta"
top104:
name: "Solar Frost Protection"
top105:
name: "Solar High Limit"
top113:
name: "Buffer Tank Delta"
top115:
name: "Water Pressure"
top116:
name: "Second Inlet Temp"
top117:
name: "Economizer Outlet Temp"
top118:
name: "Second Room Thermostat Temp"
top127:
name: "Z1 Valve PID"
top128:
name: "Z2 Valve PID"
top131:
name: "Bivalent Start Temperature"
top134:
name: "Bivalent Advanced Start Temp"
top135:
name: "Bivalent Advanced Stop Temp"
top136:
name: "Bivalent Advanced Start Delay"
top137:
name: "Bivalent Advanced Stop Delay"
top138:
name: "Bivalent Advanced DHW Delay"
xtop0:
name: "Heat Power Consumption Extra"
xtop1:
name: "Cool Power Consumption Extra"
xtop2:
name: "DHW Power Consumption Extra"
xtop3:
name: "Heat Power Production Extra"
xtop4:
name: "Cool Power Production Extra"
xtop5:
name: "DHW Power Production Extra"
# Test all binary sensors
binary_sensor:
- platform: panasonic_heatpump
top0:
name: "Heatpump State"
top2:
name: "Force DHW State"
top3:
name: "Quiet Mode Schedule"
top13:
name: "Main Schedule State"
top26:
name: "Defrosting State"
top60:
name: "Internal Heater State"
top61:
name: "External Heater State"
top68:
name: "Force Heater State"
top69:
name: "Sterilization State"
top99:
name: "Buffer Installed"
top100:
name: "DHW Installed"
top108:
name: "Alt External Sensor"
top109:
name: "Anti Freeze Mode"
top110:
name: "Optional PCB"
top119:
name: "External Control"
top120:
name: "External Heat Cool Control"
top121:
name: "External Error Signal"
top122:
name: "External Compressor Control"
top123:
name: "Z2 Pump State"
top124:
name: "Z1 Pump State"
top129:
name: "Bivalent Control"
top132:
name: "Bivalent Advanced Heat"
top133:
name: "Bivalent Advanced DHW"
# Test all text sensors
text_sensor:
- platform: panasonic_heatpump
top4:
name: "Operating Mode State"
top17:
name: "Powerful Mode Time"
top18:
name: "Quiet Mode Level"
top19:
name: "Holiday Mode State"
top20:
name: "ThreeWay Valve State"
top44:
name: "Error"
top58:
name: "DHW Heater State"
top59:
name: "Room Heater State"
top76:
name: "Heating Mode"
top81:
name: "Cooling Mode"
top92:
name: "Heat Pump Model"
top94:
name: "Zones State"
top101:
name: "Solar Mode"
top106:
name: "Pump Flowrate Mode"
top107:
name: "Liquid Type"
top111:
name: "Z1 Sensor Settings"
top112:
name: "Z2 Sensor Settings"
top114:
name: "External Pad Heater"
top125:
name: "TwoWay Valve State"
top126:
name: "ThreeWay Valve State2"
top130:
name: "Bivalent Mode"
# Test all number controls
number:
- platform: panasonic_heatpump
set5:
name: "Set Z1 Heat Request Temperature"
set6:
name: "Set Z1 Cool Request Temperature"
set7:
name: "Set Z2 Heat Request Temperature"
set8:
name: "Set Z2 Cool Request Temperature"
set11:
name: "Set DHW Temperature"
set15:
name: "Set Max Pump Duty"
set16_01:
name: "Set Zone1 Heat Target High"
set16_02:
name: "Set Zone1 Heat Target Low"
set16_03:
name: "Set Zone1 Heat Outside Low"
set16_04:
name: "Set Zone1 Heat Outside High"
set16_05:
name: "Set Zone2 Heat Target High"
set16_06:
name: "Set Zone2 Heat Target Low"
set16_07:
name: "Set Zone2 Heat Outside Low"
set16_08:
name: "Set Zone2 Heat Outside High"
set16_09:
name: "Set Zone1 Cool Target High"
set16_10:
name: "Set Zone1 Cool Target Low"
set16_11:
name: "Set Zone1 Cool Outside Low"
set16_12:
name: "Set Zone1 Cool Outside High"
set16_13:
name: "Set Zone2 Cool Target High"
set16_14:
name: "Set Zone2 Cool Target Low"
set16_15:
name: "Set Zone2 Cool Outside Low"
set16_16:
name: "Set Zone2 Cool Outside High"
set18:
name: "Set Floor Heat Delta"
set19:
name: "Set Floor Cool Delta"
set20:
name: "Set DHW Heat Delta"
set21:
name: "Set Heater Delay Time"
set22:
name: "Set Heater Start Delta"
set23:
name: "Set Heater Stop Delta"
set27:
name: "Set Buffer Delta"
set29:
name: "Set Heating Off Outdoor Temp"
set36:
name: "Set Bivalent Start Temp"
set37:
name: "Set Bivalent AP Start Temp"
set38:
name: "Set Bivalent AP Stop Temp"
# Test all select controls
select:
- platform: panasonic_heatpump
cool_mode: true
set2:
name: "Set Holiday Mode"
set3:
name: "Set Quiet Mode"
set4:
name: "Set Powerful Mode"
set9:
name: "Set Operation Mode"
set17:
name: "Set Zones"
set26:
name: "Set External PadHeater"
set35:
name: "Set Bivalent Mode"
# Test all switch controls
switch:
- platform: panasonic_heatpump
set1:
name: "Set Heatpump"
set10:
name: "Set Force DHW"
set12:
name: "Set Force Defrost"
set13:
name: "Set Force Sterilization"
set14:
name: "Set Pump Max Speed"
set24:
name: "Set Main Schedule"
set25:
name: "Set Alt External Sensor"
set28:
name: "Set Buffer Installed"
set30:
name: "Set External Control"
set31:
name: "Set External Error"
set32:
name: "Set External Compressor Control"
set33:
name: "Set External Heat Cool Control"
set34:
name: "Set Bivalent Control"
# Test climate platform
climate:
- platform: panasonic_heatpump
cool_mode: true
tank:
name: "DHW"
zone1:
name: "Zone1"
zone2:
name: "Zone2"

View File

@ -0,0 +1,136 @@
"""
Integration tests for panasonic_heatpump component.
Tests the component's behavior with actual ESPHome compilation.
"""
import pytest
import subprocess
import os
class TestPanasonicHeatpumpIntegration:
"""Integration tests using ESPHome CLI."""
@pytest.fixture
def test_yaml_esp8266(self):
"""Get the path to the ESP8266 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(base_dir, 'tests', 'panasonic_heatpump', 'test_panasonic_heatpump_esp8266.yaml')
@pytest.fixture
def test_yaml_esp32(self):
"""Get the path to the ESP32 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(base_dir, 'tests', 'panasonic_heatpump', 'test_panasonic_heatpump_full.yaml')
@pytest.fixture
def test_yaml_esp32s2(self):
"""Get the path to the ESP32-S2 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(base_dir, 'tests', 'panasonic_heatpump', 'test_panasonic_heatpump_esp32s2.yaml')
@pytest.fixture
def test_yaml_esp32c3(self):
"""Get the path to the ESP32-C3 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(base_dir, 'tests', 'panasonic_heatpump', 'test_panasonic_heatpump_esp32c3.yaml')
@pytest.fixture
def test_yaml_cztaw1(self):
"""Get the path to the CZ-TAW1 client test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(base_dir, 'tests', 'panasonic_heatpump', 'test_panasonic_heatpump_cztaw1.yaml')
def test_validate_esp8266_config(self, test_yaml_esp8266):
"""Test that the ESP8266 configuration is valid."""
if not os.path.exists(test_yaml_esp8266):
pytest.skip(f"Test file not found: {test_yaml_esp8266}")
try:
result = subprocess.run(
['esphome', 'config', test_yaml_esp8266],
capture_output=True,
text=True,
timeout=120
)
assert result.returncode == 0, f"ESP8266 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_esp32_config(self, test_yaml_esp32):
"""Test that the ESP32 configuration is valid."""
if not os.path.exists(test_yaml_esp32):
pytest.skip(f"Test file not found: {test_yaml_esp32}")
try:
result = subprocess.run(
['esphome', 'config', test_yaml_esp32],
capture_output=True,
text=True,
timeout=120
)
assert result.returncode == 0, f"ESP32 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_esp32s2_config(self, test_yaml_esp32s2):
"""Test that the ESP32-S2 configuration is valid."""
if not os.path.exists(test_yaml_esp32s2):
pytest.skip(f"Test file not found: {test_yaml_esp32s2}")
try:
result = subprocess.run(
['esphome', 'config', test_yaml_esp32s2],
capture_output=True,
text=True,
timeout=120
)
assert result.returncode == 0, f"ESP32-S2 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_esp32c3_config(self, test_yaml_esp32c3):
"""Test that the ESP32-C3 configuration is valid."""
if not os.path.exists(test_yaml_esp32c3):
pytest.skip(f"Test file not found: {test_yaml_esp32c3}")
try:
result = subprocess.run(
['esphome', 'config', test_yaml_esp32c3],
capture_output=True,
text=True,
timeout=120
)
assert result.returncode == 0, f"ESP32-C3 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_cztaw1_config(self, test_yaml_cztaw1):
"""Test that the CZ-TAW1 client configuration with UART-proxy is valid."""
if not os.path.exists(test_yaml_cztaw1):
pytest.skip(f"Test file not found: {test_yaml_cztaw1}")
try:
result = subprocess.run(
['esphome', 'config', test_yaml_cztaw1],
capture_output=True,
text=True,
timeout=120
)
assert result.returncode == 0, f"CZ-TAW1 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
if __name__ == '__main__':
pytest.main([__file__, '-v'])

View File

@ -0,0 +1,34 @@
---
# Test configuration for panasonic_heatpump component - minimal configuration
# Tests basic component functionality with minimal required settings
esp32:
board: esp32dev
framework:
type: esp-idf
esphome:
name: test-panasonic-heatpump-mini
friendly_name: "Test Panasonic Heatpump Minimal"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [panasonic_heatpump]
uart:
- id: uart_heatpump
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 9600
data_bits: 8
parity: EVEN
stop_bits: 1
# Test minimal configuration
panasonic_heatpump:
id: my_heatpump

View File

@ -0,0 +1,344 @@
"""
Unit tests for panasonic_heatpump component configuration validation.
Tests the Python configuration schema and validation logic.
"""
import pytest
from unittest.mock import Mock, MagicMock, patch
import esphome.config_validation as cv
from esphome.core import CORE
class TestPanasonicHeatpumpConfig:
"""Test suite for panasonic_heatpump configuration validation."""
@pytest.fixture(autouse=True)
def setup(self):
"""Setup test fixtures."""
# Mock CORE to avoid ESPHome initialization issues
CORE.data = {}
yield
@pytest.fixture
def mock_uart(self):
"""Mock UART component."""
with patch('esphome.components.uart') as mock:
yield mock
@pytest.fixture
def mock_cg(self):
"""Mock code generator."""
with patch('esphome.codegen') as mock:
mock.esphome_ns = MagicMock()
mock.esphome_ns.namespace.return_value.class_ = MagicMock()
yield mock
def test_import_component(self):
"""Test that the component can be imported."""
try:
import sys
import os
# Add components directory to path
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert ph_init is not None
except ImportError as e:
pytest.fail(f"Failed to import panasonic_heatpump: {e}")
def test_component_metadata(self):
"""Test component metadata is correctly defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert hasattr(ph_init, 'CODEOWNERS')
assert ph_init.CODEOWNERS == ['@elvit']
assert hasattr(ph_init, 'MULTICONF')
assert ph_init.MULTICONF is True
assert hasattr(ph_init, 'DEPENDENCIES')
assert 'uart' in ph_init.DEPENDENCIES
def test_config_constants(self):
"""Test that configuration constants are defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert hasattr(ph_init, 'CONF_PANASONIC_HEATPUMP_ID')
assert ph_init.CONF_PANASONIC_HEATPUMP_ID == "panasonic_heatpump"
assert hasattr(ph_init, 'CONF_UART_CLIENT')
assert ph_init.CONF_UART_CLIENT == "uart_client_id"
assert hasattr(ph_init, 'CONF_LOG_UART_MSG')
assert ph_init.CONF_LOG_UART_MSG == "log_uart_msg"
@patch('esphome.components.uart')
@patch('esphome.codegen')
def test_config_schema_structure(self, mock_cg, mock_uart):
"""Test that CONFIG_SCHEMA has the correct structure."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert hasattr(ph_init, 'CONFIG_SCHEMA')
assert ph_init.CONFIG_SCHEMA is not None
def test_valid_minimal_config(self):
"""Test validation of minimal valid configuration."""
config = {
'id': 'my_heatpump',
'uart_id': 'uart_bus',
}
# This would require full ESPHome environment to validate
# Just verify the config structure is reasonable
assert 'id' in config
assert 'uart_id' in config
def test_valid_full_config(self):
"""Test validation of full configuration with all options."""
config = {
'id': 'my_heatpump',
'uart_id': 'uart_bus',
'uart_client_id': 'uart_client',
'log_uart_msg': True,
'update_interval': '3s',
}
assert 'id' in config
assert 'uart_id' in config
assert 'uart_client_id' in config
assert 'log_uart_msg' in config
assert 'update_interval' in config
def test_log_uart_msg_default(self):
"""Test that log_uart_msg defaults to False."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
# The default is specified in the schema as False
# This test verifies the constant exists
assert ph_init.CONF_LOG_UART_MSG == "log_uart_msg"
def test_update_interval_default(self):
"""Test that update_interval has a default value."""
# The default update_interval should be 3s according to the example
default_interval = "3s"
assert default_interval == "3s"
def test_multiconf_support(self):
"""Test that component supports multiple instances."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
# Verify MULTICONF is True, allowing multiple instances
assert ph_init.MULTICONF is True
def test_uart_dependency(self):
"""Test that UART is listed as a dependency."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert 'uart' in ph_init.DEPENDENCIES
def test_namespace_definition(self):
"""Test that component namespace is correctly defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
# Verify namespace constant exists
assert hasattr(ph_init, 'panasonic_heatpump_ns')
class TestPanasonicHeatpumpPlatforms:
"""Test suite for platform configurations."""
def test_sensor_platform_exists(self):
"""Test that sensor platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.sensor import __init__ as sensor_init
assert sensor_init is not None
except ImportError:
pytest.skip("Sensor platform not accessible in test environment")
def test_binary_sensor_platform_exists(self):
"""Test that binary_sensor platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.binary_sensor import __init__ as bs_init
assert bs_init is not None
except ImportError:
pytest.skip("Binary sensor platform not accessible in test environment")
def test_text_sensor_platform_exists(self):
"""Test that text_sensor platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.text_sensor import __init__ as ts_init
assert ts_init is not None
except ImportError:
pytest.skip("Text sensor platform not accessible in test environment")
def test_number_platform_exists(self):
"""Test that number platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.number import __init__ as num_init
assert num_init is not None
except ImportError:
pytest.skip("Number platform not accessible in test environment")
def test_select_platform_exists(self):
"""Test that select platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.select import __init__ as sel_init
assert sel_init is not None
except ImportError:
pytest.skip("Select platform not accessible in test environment")
def test_switch_platform_exists(self):
"""Test that switch platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.switch import __init__ as sw_init
assert sw_init is not None
except ImportError:
pytest.skip("Switch platform not accessible in test environment")
def test_climate_platform_exists(self):
"""Test that climate platform can be imported."""
try:
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
from panasonic_heatpump.climate import __init__ as clim_init
assert clim_init is not None
except ImportError:
pytest.skip("Climate platform not accessible in test environment")
class TestPanasonicHeatpumpCodeGeneration:
"""Test suite for code generation logic."""
@pytest.fixture
def mock_dependencies(self):
"""Mock all ESPHome dependencies."""
with patch('esphome.codegen') as mock_cg, \
patch('esphome.components.uart') as mock_uart, \
patch('esphome.config_validation'):
mock_cg.new_Pvariable = MagicMock(return_value=Mock())
mock_cg.register_component = MagicMock()
mock_uart.register_uart_device = MagicMock()
mock_cg.get_variable = MagicMock(return_value=Mock())
mock_cg.add = MagicMock()
yield {
'cg': mock_cg,
'uart': mock_uart,
}
def test_to_code_function_exists(self):
"""Test that to_code function is defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), '..', '..', 'components'
)
sys.path.insert(0, components_path)
import panasonic_heatpump as ph_init
assert hasattr(ph_init, 'to_code')
assert callable(ph_init.to_code)
if __name__ == '__main__':
pytest.main([__file__, '-v'])

6
tests/requirements.txt Normal file
View File

@ -0,0 +1,6 @@
# Requirements for testing panasonic_heatpump component
pytest>=7.4.0
pytest-cov>=4.1.0
pytest-timeout>=2.1.0
pyyaml>=6.0.1
esphome>=2024.11.0