From 005d94c1b55a6b9cf0141156181d7ab978180d83 Mon Sep 17 00:00:00 2001 From: Conor Mongey Date: Sun, 11 Feb 2024 17:10:47 +0000 Subject: [PATCH 01/17] Add outdoor temperature sensor --- components/samsung_ac/__init__.py | 12 ++++++++++++ components/samsung_ac/protocol.h | 1 + components/samsung_ac/protocol_nasa.cpp | 8 ++++++++ components/samsung_ac/samsung_ac.h | 7 +++++++ components/samsung_ac/samsung_ac_device.h | 12 ++++++++++++ test/test_stuff.h | 9 +++++++++ 6 files changed, 49 insertions(+) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index ddef784..7bf08cd 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -50,6 +50,7 @@ CONF_DEVICE_ROOM_HUMIDITY = "room_humidity" CONF_DEVICE_TARGET_TEMPERATURE = "target_temperature" CONF_DEVICE_WATER_TEMPERATURE = "water_temperature" +CONF_DEVICE_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_DEVICE_POWER = "power" CONF_DEVICE_MODE = "mode" CONF_DEVICE_CLIMATE = "climate" @@ -71,6 +72,12 @@ device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_DEVICE_OUTDOOR_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), cv.Optional(CONF_DEVICE_ROOM_HUMIDITY): sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, accuracy_decimals=0, @@ -134,6 +141,11 @@ async def to_code(config): sens = await sensor.new_sensor(conf) cg.add(var_dev.set_room_temperature_sensor(sens)) + if CONF_DEVICE_OUTDOOR_TEMPERATURE in device: + conf = device[CONF_DEVICE_OUTDOOR_TEMPERATURE] + sens = await sensor.new_sensor(conf) + cg.add(var_dev.set_outdoor_temperature_sensor(sens)) + if CONF_DEVICE_WATER_TEMPERATURE in device: conf = device[CONF_DEVICE_WATER_TEMPERATURE] sens = await sensor.new_sensor(conf) diff --git a/components/samsung_ac/protocol.h b/components/samsung_ac/protocol.h index 9ee42f3..7f38675 100644 --- a/components/samsung_ac/protocol.h +++ b/components/samsung_ac/protocol.h @@ -71,6 +71,7 @@ namespace esphome virtual void set_water_temperature(const std::string address, float value) = 0; virtual void set_room_humidity(const std::string address, float value) = 0; virtual void set_target_temperature(const std::string address, float value) = 0; + virtual void set_outdoor_temperature(const std::string address, float value) = 0; virtual void set_mode(const std::string address, Mode mode) = 0; virtual void set_fanmode(const std::string address, FanMode fanmode) = 0; virtual void set_altmode(const std::string address, AltMode fanmode) = 0; diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 89aed10..be89b28 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -699,6 +699,14 @@ namespace esphome ESP_LOGW(TAG, "s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM_ACCUM %f", source.c_str(), dest.c_str(), value); return; } + if ((uint16_t)message.messageNumber == 0x8204) + { // VAR_OUTDOOR_TEMP + double temp = (double)message.value / (double)10; + ESP_LOGW(TAG, "s:%s d:%s VAR_OUTDOOR_TEMP %li", source.c_str(), dest.c_str(), message.value); + // possibly outdoor temperature? + target->set_outdoor_temperature(source, temp); + return; + } } } } diff --git a/components/samsung_ac/samsung_ac.h b/components/samsung_ac/samsung_ac.h index cb9a18f..4ac9770 100644 --- a/components/samsung_ac/samsung_ac.h +++ b/components/samsung_ac/samsung_ac.h @@ -82,6 +82,13 @@ namespace esphome dev->update_room_humidity(value); } + void /*MessageTarget::*/ set_outdoor_temperature(const std::string address, float value) override + { + Samsung_AC_Device *dev = find_device(address); + if (dev != nullptr) + dev->update_outdoor_temperature(value); + } + void /*MessageTarget::*/ set_target_temperature(const std::string address, float value) override { Samsung_AC_Device *dev = find_device(address); diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index fb62648..acdf456 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -78,6 +78,7 @@ namespace esphome std::string address; sensor::Sensor *room_temperature{nullptr}; sensor::Sensor *water_temperature{nullptr}; + sensor::Sensor *outdoor_temperature{nullptr}; sensor::Sensor *room_humidity{nullptr}; Samsung_AC_Number *target_temperature{nullptr}; Samsung_AC_Switch *power{nullptr}; @@ -89,6 +90,11 @@ namespace esphome room_temperature = sensor; } + void set_outdoor_temperature_sensor(sensor::Sensor *sensor) + { + outdoor_temperature = sensor; + } + void set_water_temperature_sensor(sensor::Sensor *sensor) { water_temperature = sensor; @@ -138,6 +144,12 @@ namespace esphome climate->device = this; } + void update_outdoor_temperature(float value) + { + if (outdoor_temperature != nullptr) + outdoor_temperature->publish_state(value); + } + void update_target_temperature(float value) { if (target_temperature != nullptr) diff --git a/test/test_stuff.h b/test/test_stuff.h index f59bb92..55eda90 100644 --- a/test/test_stuff.h +++ b/test/test_stuff.h @@ -69,6 +69,15 @@ class DebugTarget : public MessageTarget last_set_target_temperature_value = value; } + std::string last_set_outdoor_temperature_address; + float last_set_outdoor_temperature_value; + void set_outdoor_temperature(const std::string address, float value) + { + cout << "> " << address << " set_outdoor_temperature=" << to_string(value) << endl; + last_set_outdoor_temperature_address = address; + last_set_outdoor_temperature_value = value; + } + std::string last_set_room_humidity_address; float last_set_room_humidity_value; void set_room_humidity(const std::string address, float value) From 47ec3025c2de4ec153c8ae1c7c89e811ee4baa86 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Tue, 13 Feb 2024 10:44:25 +0100 Subject: [PATCH 02/17] Change default to stable branch --- example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.yaml b/example.yaml index 4dd6434..041bb7f 100644 --- a/example.yaml +++ b/example.yaml @@ -46,7 +46,7 @@ uart: # Import custom component from GitHub external_components: - - source: github://lanwin/esphome_samsung_ac@main + - source: github://lanwin/esphome_samsung_ac@stable # use @main if you want the latest development (possibly unstable?) components: [samsung_ac] # Configuration of AC component From b3d0c970160d20b084a1333c8a094e60370304de Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Tue, 13 Feb 2024 10:49:16 +0100 Subject: [PATCH 03/17] Add water_temperature to example.yaml --- example.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example.yaml b/example.yaml index 041bb7f..dac67f6 100644 --- a/example.yaml +++ b/example.yaml @@ -82,3 +82,7 @@ samsung_ac: room_humidity: name: "Kitchen humidity" + # Only supported on NASA based heatpumps + water_temperature: + name: "Warm water" + From b7131f514e5a54a4996c2e0e33768b47da17b2a8 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Tue, 13 Feb 2024 10:53:24 +0100 Subject: [PATCH 04/17] Switch to use enum --- components/samsung_ac/protocol_nasa.cpp | 18 ++++++++---------- components/samsung_ac/protocol_nasa.h | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index be89b28..80da8ac 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -615,18 +615,16 @@ namespace esphome target->set_swing_horizontal(source, message.value == 1); return; } - default: + case MessageNumber::VAR_in_temp_water_tank_f: { - // Test stuff - if ((uint16_t)message.messageNumber == 0x4237) - { - // VAR_IN_TEMP_WATER_TANK_F - double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_IN_TEMP_WATER_TANK_F %f", source.c_str(), dest.c_str(), temp); - target->set_water_temperature(source, temp); + double temp = (double)message.value / (double)10; + ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_water_tank_f %f", source.c_str(), dest.c_str(), temp); + target->set_water_temperature(source, temp); + return; + } - return; - } + default: + { if ((uint16_t)message.messageNumber == 0x4065) { // ENUM_IN_WATER_HEATER_POWER diff --git a/components/samsung_ac/protocol_nasa.h b/components/samsung_ac/protocol_nasa.h index baa03de..f8bab30 100644 --- a/components/samsung_ac/protocol_nasa.h +++ b/components/samsung_ac/protocol_nasa.h @@ -80,6 +80,7 @@ namespace esphome ENUM_in_state_humidity_percent = 0x4038, VAR_in_temp_room_f = 0x4203, VAR_in_temp_target_f = 0x4201, + VAR_in_temp_water_tank_f = 0x4237, }; struct Address From 2a1bb15336432ef7982e5148523baa295277bf52 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Tue, 13 Feb 2024 10:54:54 +0100 Subject: [PATCH 05/17] Switch to use enum --- components/samsung_ac/protocol_nasa.cpp | 15 +++++++-------- components/samsung_ac/protocol_nasa.h | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 80da8ac..36eefd5 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -622,6 +622,13 @@ namespace esphome target->set_water_temperature(source, temp); return; } + case MessageNumber::VAR_out_sensor_airout: + { + double temp = (double)message.value / (double)10; + ESP_LOGW(TAG, "s:%s d:%s VAR_out_sensor_airout %li", source.c_str(), dest.c_str(), message.value); + target->set_outdoor_temperature(source, temp); + return; + } default: { @@ -697,14 +704,6 @@ namespace esphome ESP_LOGW(TAG, "s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM_ACCUM %f", source.c_str(), dest.c_str(), value); return; } - if ((uint16_t)message.messageNumber == 0x8204) - { // VAR_OUTDOOR_TEMP - double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUTDOOR_TEMP %li", source.c_str(), dest.c_str(), message.value); - // possibly outdoor temperature? - target->set_outdoor_temperature(source, temp); - return; - } } } } diff --git a/components/samsung_ac/protocol_nasa.h b/components/samsung_ac/protocol_nasa.h index f8bab30..1440ddb 100644 --- a/components/samsung_ac/protocol_nasa.h +++ b/components/samsung_ac/protocol_nasa.h @@ -81,6 +81,7 @@ namespace esphome VAR_in_temp_room_f = 0x4203, VAR_in_temp_target_f = 0x4201, VAR_in_temp_water_tank_f = 0x4237, + VAR_out_sensor_airout = 0x8204, }; struct Address From 8332484dfab7ec65ab363c67531cd2c36c080dd2 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Tue, 13 Feb 2024 10:56:05 +0100 Subject: [PATCH 06/17] Add example for outdoor_temperature --- example.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example.yaml b/example.yaml index dac67f6..072969c 100644 --- a/example.yaml +++ b/example.yaml @@ -85,4 +85,6 @@ samsung_ac: # Only supported on NASA based heatpumps water_temperature: name: "Warm water" + outdoor_temperature: # Should be used with outdoor device address + name: "Outdoor temperature" From f0769f99cba0884ccf659c4304f079940da1e84c Mon Sep 17 00:00:00 2001 From: Anton Tanasenko Date: Tue, 13 Feb 2024 12:42:22 +0200 Subject: [PATCH 07/17] Support for arbitrary sensors --- components/samsung_ac/__init__.py | 53 ++++++++++++++++++++- components/samsung_ac/protocol.h | 3 ++ components/samsung_ac/protocol_nasa.cpp | 11 ++++- components/samsung_ac/samsung_ac.h | 15 ++++++ components/samsung_ac/samsung_ac_device.cpp | 1 + components/samsung_ac/samsung_ac_device.h | 30 ++++++++++++ 6 files changed, 110 insertions(+), 3 deletions(-) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index 7bf08cd..862c99b 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -8,10 +8,14 @@ DEVICE_CLASS_HUMIDITY, CONF_UNIT_OF_MEASUREMENT, CONF_DEVICE_CLASS, + CONF_FILTERS, UNIT_CELSIUS, UNIT_PERCENT, ) -from esphome.core import CORE +from esphome.core import ( + CORE, + Lambda +) CODEOWNERS = ["matthias882", "lanwin"] DEPENDENCIES = ["uart"] @@ -54,6 +58,47 @@ CONF_DEVICE_POWER = "power" CONF_DEVICE_MODE = "mode" CONF_DEVICE_CLIMATE = "climate" +CONF_DEVICE_CUSTOM = "custom_sensor" +CONF_RAW_FILTERS = "raw_filters" +CONF_DEVICE_MESSAGE = "message" +CUSTOM_SENSOR_SCHEMA = sensor.sensor_schema().extend({ + cv.Required(CONF_DEVICE_MESSAGE): cv.hex_int, +}) + +def custom_sensor_schema( + message: int, + unit_of_measurement: str = sensor._UNDEF, + icon: str = sensor._UNDEF, + accuracy_decimals: int = sensor._UNDEF, + device_class: str = sensor._UNDEF, + state_class: str = sensor._UNDEF, + entity_category: str = sensor._UNDEF, + raw_filters = [] +): + return sensor.sensor_schema( + unit_of_measurement=unit_of_measurement, + icon=icon, + accuracy_decimals=accuracy_decimals, + device_class=device_class, + state_class=state_class, + entity_category=entity_category, + ).extend({ + cv.Optional(CONF_DEVICE_MESSAGE, default=message): cv.hex_int, + cv.Optional(CONF_RAW_FILTERS, default=raw_filters): sensor.validate_filters + }) + +def temperature_sensor_schema(message: int): + return custom_sensor_schema( + message=message, + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + raw_filters=[ + { "lambda": Lambda("return (int16_t)x;") }, + { "multiply": 0.1 } + ], + ) DEVICE_SCHEMA = ( cv.Schema( @@ -88,6 +133,7 @@ cv.Optional(CONF_DEVICE_POWER): switch.switch_schema(Samsung_AC_Switch), cv.Optional(CONF_DEVICE_MODE): SELECT_MODE_SCHEMA, cv.Optional(CONF_DEVICE_CLIMATE): CLIMATE_SCHEMA, + cv.Optional(CONF_DEVICE_CUSTOM, default=[]): cv.ensure_list(CUSTOM_SENSOR_SCHEMA), } ) ) @@ -177,6 +223,11 @@ async def to_code(config): var_cli = cg.new_Pvariable(conf[CONF_ID]) await climate.register_climate(var_cli, conf) cg.add(var_dev.set_climate(var_cli)) + + if CONF_DEVICE_CUSTOM in device: + for cust_sens in device[CONF_DEVICE_CUSTOM]: + sens = await sensor.new_sensor(cust_sens) + cg.add(var_dev.add_custom_sensor(cust_sens[CONF_DEVICE_MESSAGE], sens)) cg.add(var.register_device(var_dev)) diff --git a/components/samsung_ac/protocol.h b/components/samsung_ac/protocol.h index 7f38675..265d5e9 100644 --- a/components/samsung_ac/protocol.h +++ b/components/samsung_ac/protocol.h @@ -1,5 +1,6 @@ #pragma once +#include #include "esphome/core/optional.h" #include "util.h" @@ -77,6 +78,8 @@ namespace esphome virtual void set_altmode(const std::string address, AltMode fanmode) = 0; virtual void set_swing_vertical(const std::string address, bool vertical) = 0; virtual void set_swing_horizontal(const std::string address, bool horizontal) = 0; + virtual optional> get_custom_sensors(const std::string address) = 0; + virtual void set_custom_sensor(const std::string address, uint16_t message_number, float value) = 0; }; struct ProtocolRequest diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 36eefd5..9d9fe92 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "esphome/core/log.h" #include "esphome/core/util.h" #include "esphome/core/hal.h" @@ -521,7 +522,7 @@ namespace esphome } } - void process_messageset(std::string source, std::string dest, MessageSet &message, MessageTarget *target) + void process_messageset(std::string source, std::string dest, MessageSet &message, optional> &custom, MessageTarget *target) { if (debug_mqtt_connected()) { @@ -539,6 +540,11 @@ namespace esphome } } + if (custom.has_value() && custom.value().find((uint16_t) message.messageNumber) != custom.value().end()) + { + target->set_custom_sensor(source, (uint16_t) message.messageNumber, (float) message.value); + } + switch (message.messageNumber) { case MessageNumber::VAR_in_temp_room_f: // unit = 'Celsius' from XML @@ -770,9 +776,10 @@ namespace esphome if (packet_.commad.dataType != DataType::Notification) return; + optional> custom = target->get_custom_sensors(source); for (auto &message : packet_.messages) { - process_messageset(source, dest, message, target); + process_messageset(source, dest, message, custom, target); } } diff --git a/components/samsung_ac/samsung_ac.h b/components/samsung_ac/samsung_ac.h index 4ac9770..ceb3e1c 100644 --- a/components/samsung_ac/samsung_ac.h +++ b/components/samsung_ac/samsung_ac.h @@ -138,6 +138,21 @@ namespace esphome dev->update_swing_horizontal(horizontal); } + optional> /*MessageTarget::*/ get_custom_sensors(const std::string address) override + { + Samsung_AC_Device *dev = find_device(address); + if (dev != nullptr) + return optional>(dev->get_custom_sensors()); + return optional>(); + } + + void /*MessageTarget::*/ set_custom_sensor(const std::string address, uint16_t message_number, float value) override + { + Samsung_AC_Device *dev = find_device(address); + if (dev != nullptr) + dev->update_custom_sensor(message_number, value); + } + protected: Samsung_AC_Device *find_device(const std::string address) { diff --git a/components/samsung_ac/samsung_ac_device.cpp b/components/samsung_ac/samsung_ac_device.cpp index fb03c82..df725c8 100644 --- a/components/samsung_ac/samsung_ac_device.cpp +++ b/components/samsung_ac/samsung_ac_device.cpp @@ -3,6 +3,7 @@ #include "util.h" #include "conversions.h" #include +#include namespace esphome { diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index acdf456..3764750 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -65,6 +65,12 @@ namespace esphome } }; + struct Samsung_AC_Sensor + { + uint16_t message_number; + sensor::Sensor *sensor; + }; + class Samsung_AC_Device { public: @@ -84,6 +90,7 @@ namespace esphome Samsung_AC_Switch *power{nullptr}; Samsung_AC_Mode_Select *mode{nullptr}; Samsung_AC_Climate *climate{nullptr}; + std::vector custom_sensors; void set_room_temperature_sensor(sensor::Sensor *sensor) { @@ -105,6 +112,22 @@ namespace esphome room_humidity = sensor; } + void add_custom_sensor(int message_number, sensor::Sensor *sensor) + { + Samsung_AC_Sensor cust_sensor; + cust_sensor.message_number = (uint16_t) message_number; + cust_sensor.sensor = sensor; + custom_sensors.push_back(std::move(cust_sensor)); + } + + std::set get_custom_sensors() + { + std::set numbers; + for (auto &sensor: custom_sensors) + numbers.insert(sensor.message_number); + return numbers; + } + void set_power_switch(Samsung_AC_Switch *switch_) { power = switch_; @@ -263,6 +286,13 @@ namespace esphome room_humidity->publish_state(value); } + void update_custom_sensor(uint16_t message_number, float value) + { + for (auto &sensor: custom_sensors) + if (sensor.message_number == message_number) + sensor.sensor->publish_state(value); + } + void publish_request(ProtocolRequest &request) { protocol->publish_request(target, address, request); From 7183d552ae2686f2ccd73e54cd6cba4b743b9761 Mon Sep 17 00:00:00 2001 From: Anton Tanasenko Date: Tue, 13 Feb 2024 15:25:48 +0200 Subject: [PATCH 08/17] Move nasa-specific sensors into python config --- components/samsung_ac/__init__.py | 56 +++++++++++++---------- components/samsung_ac/protocol.h | 2 - components/samsung_ac/protocol_nasa.cpp | 7 +-- components/samsung_ac/samsung_ac.h | 14 ------ components/samsung_ac/samsung_ac_device.h | 29 ++---------- 5 files changed, 37 insertions(+), 71 deletions(-) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index 862c99b..9db631e 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -51,9 +51,7 @@ CONF_DEVICE_ID = "samsung_ac_device_id" CONF_DEVICE_ADDRESS = "address" CONF_DEVICE_ROOM_TEMPERATURE = "room_temperature" -CONF_DEVICE_ROOM_HUMIDITY = "room_humidity" CONF_DEVICE_TARGET_TEMPERATURE = "target_temperature" -CONF_DEVICE_WATER_TEMPERATURE = "water_temperature" CONF_DEVICE_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_DEVICE_POWER = "power" CONF_DEVICE_MODE = "mode" @@ -61,6 +59,10 @@ CONF_DEVICE_CUSTOM = "custom_sensor" CONF_RAW_FILTERS = "raw_filters" CONF_DEVICE_MESSAGE = "message" + +CONF_DEVICE_ROOM_HUMIDITY = "room_humidity" +CONF_DEVICE_WATER_TEMPERATURE = "water_temperature" + CUSTOM_SENSOR_SCHEMA = sensor.sensor_schema().extend({ cv.Required(CONF_DEVICE_MESSAGE): cv.hex_int, }) @@ -100,6 +102,15 @@ def temperature_sensor_schema(message: int): ], ) +def humidity_sensor_schema(message: int): + return custom_sensor_schema( + message=message, + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ) + DEVICE_SCHEMA = ( cv.Schema( { @@ -111,33 +122,31 @@ def temperature_sensor_schema(message: int): device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_DEVICE_WATER_TEMPERATURE): sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_TEMPERATURE, - state_class=STATE_CLASS_MEASUREMENT, - ), cv.Optional(CONF_DEVICE_OUTDOOR_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_DEVICE_ROOM_HUMIDITY): sensor.sensor_schema( - unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=0, - device_class=DEVICE_CLASS_HUMIDITY, - state_class=STATE_CLASS_MEASUREMENT, - ), + cv.Optional(CONF_DEVICE_TARGET_TEMPERATURE): NUMBER_SCHEMA, cv.Optional(CONF_DEVICE_POWER): switch.switch_schema(Samsung_AC_Switch), cv.Optional(CONF_DEVICE_MODE): SELECT_MODE_SCHEMA, cv.Optional(CONF_DEVICE_CLIMATE): CLIMATE_SCHEMA, cv.Optional(CONF_DEVICE_CUSTOM, default=[]): cv.ensure_list(CUSTOM_SENSOR_SCHEMA), + + # keep CUSTOM_SENSOR_KEYS in sync with these + cv.Optional(CONF_DEVICE_WATER_TEMPERATURE): temperature_sensor_schema(0x4237), + cv.Optional(CONF_DEVICE_ROOM_HUMIDITY): humidity_sensor_schema(0x4038), } ) ) +CUSTOM_SENSOR_KEYS = [ + CONF_DEVICE_WATER_TEMPERATURE, + CONF_DEVICE_ROOM_HUMIDITY, +] + CONF_DEVICES = "devices" CONF_DEBUG_MQTT_HOST = "debug_mqtt_host" @@ -192,16 +201,6 @@ async def to_code(config): sens = await sensor.new_sensor(conf) cg.add(var_dev.set_outdoor_temperature_sensor(sens)) - if CONF_DEVICE_WATER_TEMPERATURE in device: - conf = device[CONF_DEVICE_WATER_TEMPERATURE] - sens = await sensor.new_sensor(conf) - cg.add(var_dev.set_water_temperature_sensor(sens)) - - if CONF_DEVICE_ROOM_HUMIDITY in device: - conf = device[CONF_DEVICE_ROOM_HUMIDITY] - sens = await sensor.new_sensor(conf) - cg.add(var_dev.set_room_humidity_sensor(sens)) - if CONF_DEVICE_TARGET_TEMPERATURE in device: conf = device[CONF_DEVICE_TARGET_TEMPERATURE] conf[CONF_UNIT_OF_MEASUREMENT] = UNIT_CELSIUS @@ -228,6 +227,15 @@ async def to_code(config): for cust_sens in device[CONF_DEVICE_CUSTOM]: sens = await sensor.new_sensor(cust_sens) cg.add(var_dev.add_custom_sensor(cust_sens[CONF_DEVICE_MESSAGE], sens)) + + for key in CUSTOM_SENSOR_KEYS: + if key in device: + conf = device[key] + # combine raw filters with any user-defined filters + conf_copy = conf.copy() + conf_copy[CONF_FILTERS] = (conf[CONF_RAW_FILTERS] if CONF_RAW_FILTERS in conf else []) + (conf[CONF_FILTERS] if CONF_FILTERS in conf else []) + sens = await sensor.new_sensor(conf_copy) + cg.add(var_dev.add_custom_sensor(conf[CONF_DEVICE_MESSAGE], sens)) cg.add(var.register_device(var_dev)) diff --git a/components/samsung_ac/protocol.h b/components/samsung_ac/protocol.h index 265d5e9..f1d977a 100644 --- a/components/samsung_ac/protocol.h +++ b/components/samsung_ac/protocol.h @@ -69,8 +69,6 @@ namespace esphome virtual void register_address(const std::string address) = 0; virtual void set_power(const std::string address, bool value) = 0; virtual void set_room_temperature(const std::string address, float value) = 0; - virtual void set_water_temperature(const std::string address, float value) = 0; - virtual void set_room_humidity(const std::string address, float value) = 0; virtual void set_target_temperature(const std::string address, float value) = 0; virtual void set_outdoor_temperature(const std::string address, float value) = 0; virtual void set_mode(const std::string address, Mode mode) = 0; diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 9d9fe92..117a714 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -566,7 +566,6 @@ namespace esphome { // XML Enum no value but in Code it adds unit ESP_LOGW(TAG, "s:%s d:%s ENUM_in_state_humidity_percent %li", source.c_str(), dest.c_str(), message.value); - target->set_room_humidity(source, message.value); return; } case MessageNumber::ENUM_in_operation_power: @@ -623,14 +622,12 @@ namespace esphome } case MessageNumber::VAR_in_temp_water_tank_f: { - double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_water_tank_f %f", source.c_str(), dest.c_str(), temp); - target->set_water_temperature(source, temp); + ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_water_tank_f %f", source.c_str(), dest.c_str(), message.value); return; } case MessageNumber::VAR_out_sensor_airout: { - double temp = (double)message.value / (double)10; + double temp = (double) ((int16_t)message.value) / (double)10; ESP_LOGW(TAG, "s:%s d:%s VAR_out_sensor_airout %li", source.c_str(), dest.c_str(), message.value); target->set_outdoor_temperature(source, temp); return; diff --git a/components/samsung_ac/samsung_ac.h b/components/samsung_ac/samsung_ac.h index ceb3e1c..a3baabd 100644 --- a/components/samsung_ac/samsung_ac.h +++ b/components/samsung_ac/samsung_ac.h @@ -68,20 +68,6 @@ namespace esphome dev->update_room_temperature(value); } - void /*MessageTarget::*/ set_water_temperature(const std::string address, float value) override - { - Samsung_AC_Device *dev = find_device(address); - if (dev != nullptr) - dev->update_water_temperature(value); - } - - void /*MessageTarget::*/ set_room_humidity(const std::string address, float value) override - { - Samsung_AC_Device *dev = find_device(address); - if (dev != nullptr) - dev->update_room_humidity(value); - } - void /*MessageTarget::*/ set_outdoor_temperature(const std::string address, float value) override { Samsung_AC_Device *dev = find_device(address); diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index 3764750..6b84bb3 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -83,9 +83,7 @@ namespace esphome std::string address; sensor::Sensor *room_temperature{nullptr}; - sensor::Sensor *water_temperature{nullptr}; sensor::Sensor *outdoor_temperature{nullptr}; - sensor::Sensor *room_humidity{nullptr}; Samsung_AC_Number *target_temperature{nullptr}; Samsung_AC_Switch *power{nullptr}; Samsung_AC_Mode_Select *mode{nullptr}; @@ -102,15 +100,6 @@ namespace esphome outdoor_temperature = sensor; } - void set_water_temperature_sensor(sensor::Sensor *sensor) - { - water_temperature = sensor; - } - - void set_room_humidity_sensor(sensor::Sensor *sensor) - { - room_humidity = sensor; - } void add_custom_sensor(int message_number, sensor::Sensor *sensor) { @@ -167,12 +156,6 @@ namespace esphome climate->device = this; } - void update_outdoor_temperature(float value) - { - if (outdoor_temperature != nullptr) - outdoor_temperature->publish_state(value); - } - void update_target_temperature(float value) { if (target_temperature != nullptr) @@ -274,16 +257,10 @@ namespace esphome } } - void update_water_temperature(float value) - { - if (water_temperature != nullptr) - water_temperature->publish_state(value); - } - - void update_room_humidity(float value) + void update_outdoor_temperature(float value) { - if (room_humidity != nullptr) - room_humidity->publish_state(value); + if (outdoor_temperature != nullptr) + outdoor_temperature->publish_state(value); } void update_custom_sensor(uint16_t message_number, float value) From 102a9572b38e3585a3c0883555fc9febdf6a0a57 Mon Sep 17 00:00:00 2001 From: Anton Tanasenko Date: Wed, 14 Feb 2024 12:58:09 +0200 Subject: [PATCH 09/17] Allow configuring alt and swing modes --- components/samsung_ac/__init__.py | 83 +++++++++++++++++++++ components/samsung_ac/conversions.cpp | 76 ++++++++----------- components/samsung_ac/conversions.h | 6 +- components/samsung_ac/protocol.h | 16 ++-- components/samsung_ac/protocol_nasa.cpp | 45 +---------- components/samsung_ac/protocol_non_nasa.cpp | 2 +- components/samsung_ac/samsung_ac_device.cpp | 62 ++++++++++----- components/samsung_ac/samsung_ac_device.h | 60 ++++++++++++++- 8 files changed, 226 insertions(+), 124 deletions(-) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index 9db631e..29c84fd 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -50,6 +50,13 @@ CONF_DEVICE_ID = "samsung_ac_device_id" CONF_DEVICE_ADDRESS = "address" +CONF_CAPABILITIES = "capabilities" +CONF_HORIZONTAL_SWING = "horizontal_swing" +CONF_VERTICAL_SWING = "vertical_swing" +CONF_PRESETS = "presets" +CONF_PRESET_NAME = "name" +CONF_PRESET_ENABLED = "enabled" +CONF_PRESET_VALUE = "value" CONF_DEVICE_ROOM_TEMPERATURE = "room_temperature" CONF_DEVICE_TARGET_TEMPERATURE = "target_temperature" CONF_DEVICE_OUTDOOR_TEMPERATURE = "outdoor_temperature" @@ -63,6 +70,36 @@ CONF_DEVICE_ROOM_HUMIDITY = "room_humidity" CONF_DEVICE_WATER_TEMPERATURE = "water_temperature" +def preset_entry( + name: str, + value: int, + displayName: str +): return ( + cv.Optional(name, default=False), cv.Any(cv.boolean, cv.All({ + cv.Optional(CONF_PRESET_ENABLED, default=False): cv.boolean, + cv.Optional(CONF_PRESET_NAME, default=displayName): cv.string, + cv.Optional(CONF_PRESET_VALUE, default=value): cv.int_ + })) +) + +PRESETS = { + "sleep": {"value": 1, "displayName": "Sleep"}, + "quiet": {"value": 2, "displayName": "Quiet"}, + "fast": {"value": 3, "displayName": "Fast"}, + "longreach": {"value": 6, "displayName": "LongReach"}, + "windfree": {"value": 9, "displayName": "WindFree"}, +} + +CAPABILITIES_SCHEMA = ( + cv.Schema({ + cv.Optional(CONF_HORIZONTAL_SWING, default=False): cv.boolean, + cv.Optional(CONF_VERTICAL_SWING, default=False): cv.boolean, + cv.Optional(CONF_PRESETS): cv.Schema(dict( + [preset_entry(name, PRESETS[name]["value"], PRESETS[name]["displayName"]) for name in PRESETS] + )) + }) +) + CUSTOM_SENSOR_SCHEMA = sensor.sensor_schema().extend({ cv.Required(CONF_DEVICE_MESSAGE): cv.hex_int, }) @@ -115,6 +152,7 @@ def humidity_sensor_schema(message: int): cv.Schema( { cv.GenerateID(CONF_DEVICE_ID): cv.declare_id(Samsung_AC_Device), + cv.Optional(CONF_CAPABILITIES): CAPABILITIES_SCHEMA, cv.Required(CONF_DEVICE_ADDRESS): cv.string, cv.Optional(CONF_DEVICE_ROOM_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, @@ -168,6 +206,7 @@ def humidity_sensor_schema(message: int): cv.Optional(CONF_DEBUG_MQTT_PASSWORD, default=""): cv.string, cv.Optional(CONF_DEBUG_LOG_MESSAGES, default=False): cv.boolean, cv.Optional(CONF_DEBUG_LOG_MESSAGES_RAW, default=False): cv.boolean, + cv.Optional(CONF_CAPABILITIES): CAPABILITIES_SCHEMA, cv.Required(CONF_DEVICES): cv.ensure_list(DEVICE_SCHEMA), } ) @@ -186,6 +225,50 @@ async def to_code(config): var_dev = cg.new_Pvariable( device[CONF_DEVICE_ID], device[CONF_DEVICE_ADDRESS], var) + # setup capabilities + if CONF_CAPABILITIES in device and CONF_VERTICAL_SWING in device[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_vertical_swing(device[CONF_CAPABILITIES][CONF_VERTICAL_SWING])) + elif CONF_CAPABILITIES in config and CONF_VERTICAL_SWING in config[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_vertical_swing(config[CONF_CAPABILITIES][CONF_VERTICAL_SWING])) + + if CONF_CAPABILITIES in device and CONF_HORIZONTAL_SWING in device[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_horizontal_swing(device[CONF_CAPABILITIES][CONF_HORIZONTAL_SWING])) + elif CONF_CAPABILITIES in config and CONF_HORIZONTAL_SWING in config[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_horizontal_swing(config[CONF_CAPABILITIES][CONF_HORIZONTAL_SWING])) + + + none_added = False + for preset in PRESETS: + device_preset_conf = device[CONF_CAPABILITIES][CONF_PRESETS][preset] if ( + CONF_CAPABILITIES in device + and CONF_PRESETS in device[CONF_CAPABILITIES] + and preset in device[CONF_CAPABILITIES][CONF_PRESETS]) else None + global_preset_conf = config[CONF_CAPABILITIES][CONF_PRESETS][preset] if ( + CONF_CAPABILITIES in config + and CONF_PRESETS in config[CONF_CAPABILITIES] + and preset in config[CONF_CAPABILITIES][CONF_PRESETS]) else None + + preset_conf = global_preset_conf if device_preset_conf is None else device_preset_conf + preset_dict = isinstance(preset_conf, dict) + if preset_conf == True or (preset_dict and preset_conf[CONF_PRESET_ENABLED] == True): + if not none_added: + none_added = True + cg.add(var_dev.add_alt_mode("None", 0)) + + cg.add(var_dev.add_alt_mode( + preset_conf[CONF_PRESET_NAME] if preset_dict and CONF_PRESET_NAME in preset_conf else PRESETS[preset]["displayName"], + preset_conf[CONF_PRESET_VALUE] if preset_dict and CONF_PRESET_VALUE in preset_conf else PRESETS[preset]["value"] + )) + +# if CONF_CAPABILITIES in device and CONF_ALT_MODES in device[CONF_CAPABILITIES]: +# cg.add(var_dev.add_alt_mode("None", 0)) +# for alt in device[CONF_CAPABILITIES][CONF_ALT_MODES]: +# cg.add(var_dev.add_alt_mode(alt[CONF_ALT_MODE_NAME], alt[CONF_ALT_MODE_VALUE])) +# elif CONF_CAPABILITIES in config and CONF_ALT_MODES in config[CONF_CAPABILITIES]: +# cg.add(var_dev.add_alt_mode("None", 0)) +# for alt in config[CONF_CAPABILITIES][CONF_ALT_MODES]: +# cg.add(var_dev.add_alt_mode(alt[CONF_ALT_MODE_NAME], alt[CONF_ALT_MODE_VALUE])) + if CONF_DEVICE_POWER in device: conf = device[CONF_DEVICE_POWER] sens = await switch.new_switch(conf) diff --git a/components/samsung_ac/conversions.cpp b/components/samsung_ac/conversions.cpp index eedc172..ba6ee24 100644 --- a/components/samsung_ac/conversions.cpp +++ b/components/samsung_ac/conversions.cpp @@ -128,59 +128,49 @@ namespace esphome return FanMode::Auto; } - AltMode preset_to_altmode(climate::ClimatePreset preset) + AltModeName preset_to_altmodename(climate::ClimatePreset preset) { switch (preset) { + case climate::ClimatePreset::CLIMATE_PRESET_ECO: + return "Eco"; + case climate::ClimatePreset::CLIMATE_PRESET_AWAY: + return "Away"; + case climate::ClimatePreset::CLIMATE_PRESET_BOOST: + return "Boost"; + case climate::ClimatePreset::CLIMATE_PRESET_COMFORT: + return "Comfort"; + case climate::ClimatePreset::CLIMATE_PRESET_HOME: + return "Home"; case climate::ClimatePreset::CLIMATE_PRESET_SLEEP: - return AltMode::Sleep; + return "Sleep"; + case climate::ClimatePreset::CLIMATE_PRESET_ACTIVITY: + return "Activity"; case climate::ClimatePreset::CLIMATE_PRESET_NONE: default: - return AltMode::None; + return "None"; } } - AltMode custompreset_to_altmode(const std::string &value) + optional altmodename_to_preset(const AltModeName& name) { - if (value == "Quiet") - return AltMode::Quiet; - if (value == "Fast") - return AltMode::Fast; - if (value == "Long Reach") - return AltMode::LongReach; - if (value == "WindFree") - return AltMode::Windfree; - return AltMode::Unknown; - } - - optional altmode_to_preset(AltMode mode) - { - switch (mode) - { - case AltMode::None: - return climate::ClimatePreset::CLIMATE_PRESET_NONE; - case AltMode::Sleep: - return climate::ClimatePreset::CLIMATE_PRESET_SLEEP; - default: - return nullopt; - }; - } - - std::string altmode_to_custompreset(AltMode mode) - { - switch (mode) - { - case AltMode::Quiet: - return "Quiet"; - case AltMode::Fast: - return "Fast"; - case AltMode::LongReach: - return "Long Reach"; - case AltMode::Windfree: - return "WindFree"; - default: - return ""; - }; + if (str_equals_case_insensitive(name, "ECO")) + return optional(climate::CLIMATE_PRESET_ECO); + if (str_equals_case_insensitive(name, "AWAY")) + return optional(climate::CLIMATE_PRESET_AWAY); + if (str_equals_case_insensitive(name, "BOOST")) + return optional(climate::CLIMATE_PRESET_BOOST); + if (str_equals_case_insensitive(name, "COMFORT")) + return optional(climate::CLIMATE_PRESET_COMFORT); + if (str_equals_case_insensitive(name, "HOME")) + return optional(climate::CLIMATE_PRESET_HOME); + if (str_equals_case_insensitive(name, "SLEEP")) + return optional(climate::CLIMATE_PRESET_SLEEP); + if (str_equals_case_insensitive(name, "ACTIVITY")) + return optional(climate::CLIMATE_PRESET_ACTIVITY); + if (str_equals_case_insensitive(name, "NONE")) + return optional(climate::CLIMATE_PRESET_NONE); + return nullopt; } climate::ClimateSwingMode swingmode_to_climateswingmode(SwingMode swingMode) diff --git a/components/samsung_ac/conversions.h b/components/samsung_ac/conversions.h index f38c22e..8bfb621 100644 --- a/components/samsung_ac/conversions.h +++ b/components/samsung_ac/conversions.h @@ -19,10 +19,8 @@ namespace esphome FanMode climatefanmode_to_fanmode(climate::ClimateFanMode fanmode); FanMode customfanmode_to_fanmode(const std::string &value); - optional altmode_to_preset(AltMode mode); - std::string altmode_to_custompreset(AltMode mode); - AltMode preset_to_altmode(climate::ClimatePreset preset); - AltMode custompreset_to_altmode(const std::string &value); + AltModeName preset_to_altmodename(climate::ClimatePreset preset); + optional altmodename_to_preset(const AltModeName& name); climate::ClimateSwingMode swingmode_to_climateswingmode(SwingMode swingMode); SwingMode climateswingmode_to_swingmode(climate::ClimateSwingMode swingMode); diff --git a/components/samsung_ac/protocol.h b/components/samsung_ac/protocol.h index f1d977a..cac09ee 100644 --- a/components/samsung_ac/protocol.h +++ b/components/samsung_ac/protocol.h @@ -42,15 +42,13 @@ namespace esphome Off = 5 }; - enum class AltMode + typedef std::string AltModeName; + typedef uint8_t AltMode; + + struct AltModeDesc { - Unknown = -1, - None = 0, - Sleep = 1, - Quiet = 2, - Fast = 3, - LongReach = 4, - Windfree = 5 + AltModeName name; + AltMode value; }; enum class SwingMode : uint8_t @@ -73,7 +71,7 @@ namespace esphome virtual void set_outdoor_temperature(const std::string address, float value) = 0; virtual void set_mode(const std::string address, Mode mode) = 0; virtual void set_fanmode(const std::string address, FanMode fanmode) = 0; - virtual void set_altmode(const std::string address, AltMode fanmode) = 0; + virtual void set_altmode(const std::string address, AltMode altmode) = 0; virtual void set_swing_vertical(const std::string address, bool vertical) = 0; virtual void set_swing_horizontal(const std::string address, bool horizontal) = 0; virtual optional> get_custom_sensors(const std::string address) = 0; diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 117a714..970a916 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -367,26 +367,6 @@ namespace esphome } } - int altmode_to_nasa_altmode(AltMode mode) - { - switch (mode) - { - case AltMode::Sleep: - return 1; - case AltMode::Quiet: - return 2; - case AltMode::Fast: - return 3; - case AltMode::LongReach: - return 6; - case AltMode::Windfree: - return 9; - case AltMode::None: - default: - return 0; - } - } - void NasaProtocol::publish_request(MessageTarget *target, const std::string &address, ProtocolRequest &request) { Packet packet = Packet::createa_partial(Address::parse(address), DataType::Request); @@ -424,7 +404,7 @@ namespace esphome if (request.alt_mode) { MessageSet altmode(MessageNumber::ENUM_in_alt_mode); - altmode.value = altmode_to_nasa_altmode(request.alt_mode.value()); + altmode.value = request.alt_mode.value(); packet.messages.push_back(altmode); } @@ -501,27 +481,6 @@ namespace esphome } } - AltMode altmode_to_nasa_altmode(int value) - { - switch (value) - { - case 0: - return AltMode::None; - case 1: - return AltMode::Sleep; - case 2: - return AltMode::Quiet; - case 3: - return AltMode::Fast; - case 6: - return AltMode::LongReach; - case 9: - return AltMode::Windfree; - default: - return AltMode::Unknown; - } - } - void process_messageset(std::string source, std::string dest, MessageSet &message, optional> &custom, MessageTarget *target) { if (debug_mqtt_connected()) @@ -605,7 +564,7 @@ namespace esphome case MessageNumber::ENUM_in_alt_mode: { ESP_LOGW(TAG, "s:%s d:%s ENUM_in_alt_mode %li", source.c_str(), dest.c_str(), message.value); - target->set_altmode(source, altmode_to_nasa_altmode(message.value)); + target->set_altmode(source, message.value); return; } case MessageNumber::ENUM_in_louver_hl_swing: diff --git a/components/samsung_ac/protocol_non_nasa.cpp b/components/samsung_ac/protocol_non_nasa.cpp index cff2907..48cd139 100644 --- a/components/samsung_ac/protocol_non_nasa.cpp +++ b/components/samsung_ac/protocol_non_nasa.cpp @@ -497,7 +497,7 @@ namespace esphome target->set_mode(nonpacket_.src, nonnasa_mode_to_mode(nonpacket_.command20.mode)); target->set_fanmode(nonpacket_.src, nonnasa_fanspeed_to_fanmode(nonpacket_.command20.fanspeed)); // TODO - target->set_altmode(nonpacket_.src, AltMode::None); + target->set_altmode(nonpacket_.src, 0); // TODO target->set_swing_horizontal(nonpacket_.src, false); target->set_swing_vertical(nonpacket_.src, false); diff --git a/components/samsung_ac/samsung_ac_device.cpp b/components/samsung_ac/samsung_ac_device.cpp index df725c8..b3803a3 100644 --- a/components/samsung_ac/samsung_ac_device.cpp +++ b/components/samsung_ac/samsung_ac_device.cpp @@ -4,6 +4,7 @@ #include "conversions.h" #include #include +#include namespace esphome { @@ -49,24 +50,33 @@ namespace esphome customFan.insert("Turbo"); traits.set_supported_custom_fan_modes(customFan); - std::set preset; - preset.insert(climate::ClimatePreset::CLIMATE_PRESET_NONE); - preset.insert(climate::ClimatePreset::CLIMATE_PRESET_SLEEP); - traits.set_supported_presets(preset); - - std::set customPreset; - customPreset.insert("Quiet"); - customPreset.insert("Fast"); - customPreset.insert("Long Reach"); - customPreset.insert("WindFree"); - traits.set_supported_custom_presets(customPreset); - - std::set swingMode; - swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_OFF); - swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_HORIZONTAL); - swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_VERTICAL); - swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_BOTH); - traits.set_supported_swing_modes(swingMode); + auto supported = device->get_supported_alt_modes(); + if (supported != nullptr && !supported->empty()) + { + std::set presets; + std::set custom_presets; + for (const AltModeDesc& mode: *supported) { + auto preset = altmodename_to_preset(mode.name); + if (preset) + presets.insert(preset.value()); + else + custom_presets.insert(mode.name); + }; + traits.set_supported_presets(presets); + traits.set_supported_custom_presets(custom_presets); + } + + bool h = device->supports_horizontal_swing(); + bool v = device->supports_vertical_swing(); + if (h || v) + { + std::set swingMode; + swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_OFF); + if (h) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_HORIZONTAL); + if (v) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_VERTICAL); + if (h && v) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_BOTH); + traits.set_supported_swing_modes(swingMode); + } return traits; } @@ -109,13 +119,13 @@ namespace esphome auto presetOpt = call.get_preset(); if (presetOpt.has_value()) { - request.alt_mode = preset_to_altmode(presetOpt.value()); + set_alt_mode_by_name(request, preset_to_altmodename(presetOpt.value())); } auto customPresetOpt = call.get_custom_preset(); if (customPresetOpt.has_value()) { - request.alt_mode = custompreset_to_altmode(customPresetOpt.value()); + set_alt_mode_by_name(request, customPresetOpt.value()); } auto swingModeOpt = call.get_swing_mode(); @@ -126,5 +136,17 @@ namespace esphome device->publish_request(request); } + + void Samsung_AC_Climate::set_alt_mode_by_name(ProtocolRequest &request, const AltModeName &name) + { + auto supported = device->get_supported_alt_modes(); + auto mode = std::find_if(supported->begin(), supported->end(), [&name](const AltModeDesc &x) { return x.name == name;}); + if (mode == supported->end()) + { + ESP_LOGW(TAG, "Unsupported alt_mode %s", name); + return; + } + request.alt_mode = mode->value; + } } // namespace samsung_ac } // namespace esphome diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index 6b84bb3..3c8c791 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -2,6 +2,8 @@ #include #include +#include +#include "esphome/core/helpers.h" #include "esphome/components/switch/switch.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/select/select.h" @@ -24,6 +26,9 @@ namespace esphome climate::ClimateTraits traits(); void control(const climate::ClimateCall &call); Samsung_AC_Device *device; + + protected: + void set_alt_mode_by_name(ProtocolRequest &request, const AltModeName &name); }; class Samsung_AC_Number : public number::Number @@ -213,16 +218,24 @@ namespace esphome { if (climate != nullptr) { - auto preset = altmode_to_preset(value); - if (preset.has_value()) + auto supported = get_supported_alt_modes(); + auto mode = std::find_if(supported->begin(), supported->end(), [&value](const AltModeDesc& x) { return x.value == value; }); + if (mode == supported->end()) + { + ESP_LOGW(TAG, "Unsupported alt_mode %d", value); + return; + } + + auto preset = altmodename_to_preset(mode->name); + if (preset) { - climate->preset = preset; + climate->preset = preset.value(); climate->custom_preset.reset(); } else { climate->preset.reset(); - climate->custom_preset = altmode_to_custompreset(value); + climate->custom_preset = mode->name; } climate->publish_state(); } @@ -275,7 +288,46 @@ namespace esphome protocol->publish_request(target, address, request); } + bool supports_horizontal_swing() + { + return supports_horizontal_swing_; + } + + bool supports_vertical_swing() + { + return supports_vertical_swing_; + } + + void set_supports_horizontal_swing(bool value) + { + supports_horizontal_swing_ = value; + } + + void set_supports_vertical_swing(bool value) + { + supports_vertical_swing_ = value; + } + + void add_alt_mode(const AltModeName &name, AltMode value) + { + AltModeDesc desc; + desc.name = name; + desc.value = value; + alt_modes.push_back(std::move(desc)); + } + + const std::vector *get_supported_alt_modes() + { + if (!alt_modes.empty()) + return &alt_modes; + return nullptr; + } + protected: + bool supports_horizontal_swing_{true}; + bool supports_vertical_swing_{true}; + std::vector alt_modes; + Protocol *protocol{nullptr}; MessageTarget *target{nullptr}; From f67f0bc1c515bdc7fd81458a8f41c066fa6bdcd0 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 15:02:30 +0100 Subject: [PATCH 10/17] Fix: make tests compile --- components/samsung_ac/protocol_nasa.cpp | 6 +++--- test/test_stuff.h | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 970a916..5ec3432 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -499,9 +499,9 @@ namespace esphome } } - if (custom.has_value() && custom.value().find((uint16_t) message.messageNumber) != custom.value().end()) + if (custom && custom.value().find((uint16_t)message.messageNumber) != custom.value().end()) { - target->set_custom_sensor(source, (uint16_t) message.messageNumber, (float) message.value); + target->set_custom_sensor(source, (uint16_t)message.messageNumber, (float)message.value); } switch (message.messageNumber) @@ -586,7 +586,7 @@ namespace esphome } case MessageNumber::VAR_out_sensor_airout: { - double temp = (double) ((int16_t)message.value) / (double)10; + double temp = (double)((int16_t)message.value) / (double)10; ESP_LOGW(TAG, "s:%s d:%s VAR_out_sensor_airout %li", source.c_str(), dest.c_str(), message.value); target->set_outdoor_temperature(source, temp); return; diff --git a/test/test_stuff.h b/test/test_stuff.h index 55eda90..ce8b169 100644 --- a/test/test_stuff.h +++ b/test/test_stuff.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include "esphome/core/optional.h" #include "../components/samsung_ac/util.h" #include "../components/samsung_ac/protocol.h" @@ -105,16 +105,31 @@ class DebugTarget : public MessageTarget last_set_fanmode_mode = fanmode; } - void set_altmode(const std::string address, AltMode fanmode) + void set_altmode(const std::string address, AltMode altmode) { + cout << "> " << address << " set_altmode=" << to_string((int)altmode) << endl; } void set_swing_vertical(const std::string address, bool vertical) { + cout << "> " << address << " set_swing_vertical=" << to_string((int)vertical) << endl; } void set_swing_horizontal(const std::string address, bool horizontal) { + cout << "> " << address << " set_swing_horizontal=" << to_string((int)horizontal) << endl; + } + + std::set last_custom_sensors; + + esphome::optional> get_custom_sensors(const std::string address) + { + return last_custom_sensors; + } + + void set_custom_sensor(const std::string address, uint16_t message_number, float value) + { + last_custom_sensors.insert(message_number); } void assert_only_address(const std::string address) From b6c5a2460c8c55038ad1c68eb88703999493bfd4 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 15:12:27 +0100 Subject: [PATCH 11/17] Namespace py objects a little bit --- components/samsung_ac/__init__.py | 106 +++++++++++++++++------------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index 29c84fd..b4e5d17 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -13,7 +13,7 @@ UNIT_PERCENT, ) from esphome.core import ( - CORE, + CORE, Lambda ) @@ -50,30 +50,32 @@ CONF_DEVICE_ID = "samsung_ac_device_id" CONF_DEVICE_ADDRESS = "address" -CONF_CAPABILITIES = "capabilities" -CONF_HORIZONTAL_SWING = "horizontal_swing" -CONF_VERTICAL_SWING = "vertical_swing" -CONF_PRESETS = "presets" -CONF_PRESET_NAME = "name" -CONF_PRESET_ENABLED = "enabled" -CONF_PRESET_VALUE = "value" CONF_DEVICE_ROOM_TEMPERATURE = "room_temperature" CONF_DEVICE_TARGET_TEMPERATURE = "target_temperature" CONF_DEVICE_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_DEVICE_POWER = "power" CONF_DEVICE_MODE = "mode" CONF_DEVICE_CLIMATE = "climate" -CONF_DEVICE_CUSTOM = "custom_sensor" -CONF_RAW_FILTERS = "raw_filters" -CONF_DEVICE_MESSAGE = "message" - CONF_DEVICE_ROOM_HUMIDITY = "room_humidity" CONF_DEVICE_WATER_TEMPERATURE = "water_temperature" +CONF_DEVICE_CUSTOM = "custom_sensor" +CONF_DEVICE_CUSTOM_MESSAGE = "message" +CONF_DEVICE_CUSTOM_RAW_FILTERS = "raw_filters" + +CONF_CAPABILITIES = "capabilities" +CONF_CAPABILITIES_HORIZONTAL_SWING = "horizontal_swing" +CONF_CAPABILITIES_VERTICAL_SWING = "vertical_swing" + +CONF_PRESETS = "presets" +CONF_PRESET_NAME = "name" +CONF_PRESET_ENABLED = "enabled" +CONF_PRESET_VALUE = "value" + def preset_entry( - name: str, - value: int, - displayName: str + name: str, + value: int, + displayName: str ): return ( cv.Optional(name, default=False), cv.Any(cv.boolean, cv.All({ cv.Optional(CONF_PRESET_ENABLED, default=False): cv.boolean, @@ -82,6 +84,7 @@ def preset_entry( })) ) + PRESETS = { "sleep": {"value": 1, "displayName": "Sleep"}, "quiet": {"value": 2, "displayName": "Quiet"}, @@ -92,18 +95,20 @@ def preset_entry( CAPABILITIES_SCHEMA = ( cv.Schema({ - cv.Optional(CONF_HORIZONTAL_SWING, default=False): cv.boolean, - cv.Optional(CONF_VERTICAL_SWING, default=False): cv.boolean, + cv.Optional(CONF_CAPABILITIES_HORIZONTAL_SWING, default=False): cv.boolean, + cv.Optional(CONF_CAPABILITIES_VERTICAL_SWING, default=False): cv.boolean, cv.Optional(CONF_PRESETS): cv.Schema(dict( - [preset_entry(name, PRESETS[name]["value"], PRESETS[name]["displayName"]) for name in PRESETS] + [preset_entry(name, PRESETS[name]["value"], + PRESETS[name]["displayName"]) for name in PRESETS] )) }) ) CUSTOM_SENSOR_SCHEMA = sensor.sensor_schema().extend({ - cv.Required(CONF_DEVICE_MESSAGE): cv.hex_int, + cv.Required(CONF_DEVICE_CUSTOM_MESSAGE): cv.hex_int, }) + def custom_sensor_schema( message: int, unit_of_measurement: str = sensor._UNDEF, @@ -112,7 +117,7 @@ def custom_sensor_schema( device_class: str = sensor._UNDEF, state_class: str = sensor._UNDEF, entity_category: str = sensor._UNDEF, - raw_filters = [] + raw_filters=[] ): return sensor.sensor_schema( unit_of_measurement=unit_of_measurement, @@ -122,10 +127,11 @@ def custom_sensor_schema( state_class=state_class, entity_category=entity_category, ).extend({ - cv.Optional(CONF_DEVICE_MESSAGE, default=message): cv.hex_int, - cv.Optional(CONF_RAW_FILTERS, default=raw_filters): sensor.validate_filters + cv.Optional(CONF_DEVICE_CUSTOM_MESSAGE, default=message): cv.hex_int, + cv.Optional(CONF_DEVICE_CUSTOM_RAW_FILTERS, default=raw_filters): sensor.validate_filters }) + def temperature_sensor_schema(message: int): return custom_sensor_schema( message=message, @@ -134,11 +140,12 @@ def temperature_sensor_schema(message: int): device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, raw_filters=[ - { "lambda": Lambda("return (int16_t)x;") }, - { "multiply": 0.1 } + {"lambda": Lambda("return (int16_t)x;")}, + {"multiply": 0.1} ], ) + def humidity_sensor_schema(message: int): return custom_sensor_schema( message=message, @@ -148,6 +155,7 @@ def humidity_sensor_schema(message: int): state_class=STATE_CLASS_MEASUREMENT, ) + DEVICE_SCHEMA = ( cv.Schema( { @@ -226,28 +234,31 @@ async def to_code(config): device[CONF_DEVICE_ID], device[CONF_DEVICE_ADDRESS], var) # setup capabilities - if CONF_CAPABILITIES in device and CONF_VERTICAL_SWING in device[CONF_CAPABILITIES]: - cg.add(var_dev.set_supports_vertical_swing(device[CONF_CAPABILITIES][CONF_VERTICAL_SWING])) - elif CONF_CAPABILITIES in config and CONF_VERTICAL_SWING in config[CONF_CAPABILITIES]: - cg.add(var_dev.set_supports_vertical_swing(config[CONF_CAPABILITIES][CONF_VERTICAL_SWING])) - - if CONF_CAPABILITIES in device and CONF_HORIZONTAL_SWING in device[CONF_CAPABILITIES]: - cg.add(var_dev.set_supports_horizontal_swing(device[CONF_CAPABILITIES][CONF_HORIZONTAL_SWING])) - elif CONF_CAPABILITIES in config and CONF_HORIZONTAL_SWING in config[CONF_CAPABILITIES]: - cg.add(var_dev.set_supports_horizontal_swing(config[CONF_CAPABILITIES][CONF_HORIZONTAL_SWING])) - + if CONF_CAPABILITIES in device and CONF_CAPABILITIES_VERTICAL_SWING in device[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_vertical_swing( + device[CONF_CAPABILITIES][CONF_CAPABILITIES_VERTICAL_SWING])) + elif CONF_CAPABILITIES in config and CONF_CAPABILITIES_VERTICAL_SWING in config[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_vertical_swing( + config[CONF_CAPABILITIES][CONF_CAPABILITIES_VERTICAL_SWING])) + + if CONF_CAPABILITIES in device and CONF_CAPABILITIES_HORIZONTAL_SWING in device[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_horizontal_swing( + device[CONF_CAPABILITIES][CONF_CAPABILITIES_HORIZONTAL_SWING])) + elif CONF_CAPABILITIES in config and CONF_CAPABILITIES_HORIZONTAL_SWING in config[CONF_CAPABILITIES]: + cg.add(var_dev.set_supports_horizontal_swing( + config[CONF_CAPABILITIES][CONF_CAPABILITIES_HORIZONTAL_SWING])) none_added = False for preset in PRESETS: device_preset_conf = device[CONF_CAPABILITIES][CONF_PRESETS][preset] if ( - CONF_CAPABILITIES in device - and CONF_PRESETS in device[CONF_CAPABILITIES] + CONF_CAPABILITIES in device + and CONF_PRESETS in device[CONF_CAPABILITIES] and preset in device[CONF_CAPABILITIES][CONF_PRESETS]) else None global_preset_conf = config[CONF_CAPABILITIES][CONF_PRESETS][preset] if ( - CONF_CAPABILITIES in config - and CONF_PRESETS in config[CONF_CAPABILITIES] + CONF_CAPABILITIES in config + and CONF_PRESETS in config[CONF_CAPABILITIES] and preset in config[CONF_CAPABILITIES][CONF_PRESETS]) else None - + preset_conf = global_preset_conf if device_preset_conf is None else device_preset_conf preset_dict = isinstance(preset_conf, dict) if preset_conf == True or (preset_dict and preset_conf[CONF_PRESET_ENABLED] == True): @@ -256,7 +267,7 @@ async def to_code(config): cg.add(var_dev.add_alt_mode("None", 0)) cg.add(var_dev.add_alt_mode( - preset_conf[CONF_PRESET_NAME] if preset_dict and CONF_PRESET_NAME in preset_conf else PRESETS[preset]["displayName"], + preset_conf[CONF_PRESET_NAME] if preset_dict and CONF_PRESET_NAME in preset_conf else PRESETS[preset]["displayName"], preset_conf[CONF_PRESET_VALUE] if preset_dict and CONF_PRESET_VALUE in preset_conf else PRESETS[preset]["value"] )) @@ -268,7 +279,7 @@ async def to_code(config): # cg.add(var_dev.add_alt_mode("None", 0)) # for alt in config[CONF_CAPABILITIES][CONF_ALT_MODES]: # cg.add(var_dev.add_alt_mode(alt[CONF_ALT_MODE_NAME], alt[CONF_ALT_MODE_VALUE])) - + if CONF_DEVICE_POWER in device: conf = device[CONF_DEVICE_POWER] sens = await switch.new_switch(conf) @@ -305,20 +316,23 @@ async def to_code(config): var_cli = cg.new_Pvariable(conf[CONF_ID]) await climate.register_climate(var_cli, conf) cg.add(var_dev.set_climate(var_cli)) - + if CONF_DEVICE_CUSTOM in device: for cust_sens in device[CONF_DEVICE_CUSTOM]: sens = await sensor.new_sensor(cust_sens) - cg.add(var_dev.add_custom_sensor(cust_sens[CONF_DEVICE_MESSAGE], sens)) - + cg.add(var_dev.add_custom_sensor( + cust_sens[CONF_DEVICE_CUSTOM_MESSAGE], sens)) + for key in CUSTOM_SENSOR_KEYS: if key in device: conf = device[key] # combine raw filters with any user-defined filters conf_copy = conf.copy() - conf_copy[CONF_FILTERS] = (conf[CONF_RAW_FILTERS] if CONF_RAW_FILTERS in conf else []) + (conf[CONF_FILTERS] if CONF_FILTERS in conf else []) + conf_copy[CONF_FILTERS] = (conf[CONF_DEVICE_CUSTOM_RAW_FILTERS] if CONF_DEVICE_CUSTOM_RAW_FILTERS in conf else [ + ]) + (conf[CONF_FILTERS] if CONF_FILTERS in conf else []) sens = await sensor.new_sensor(conf_copy) - cg.add(var_dev.add_custom_sensor(conf[CONF_DEVICE_MESSAGE], sens)) + cg.add(var_dev.add_custom_sensor( + conf[CONF_DEVICE_CUSTOM_MESSAGE], sens)) cg.add(var.register_device(var_dev)) From 47f9ba43410f21ee4ae3757c748a9ea6f8c769b3 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 15:29:05 +0100 Subject: [PATCH 12/17] Add capabilities to example.yaml --- example.yaml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/example.yaml b/example.yaml index 072969c..b0b57e6 100644 --- a/example.yaml +++ b/example.yaml @@ -51,6 +51,23 @@ external_components: # Configuration of AC component samsung_ac: + + # Capabilities configure the features alle devices of your AC system have (all parts of this section are optional). + # All capabilities are off by default, you need to enable only those your devices have. + # You can override or configure them also on a per devices basis (look below for that). + capabilities: + vertical_swing: true + horizontal_swing: true + # Presets define special AC modes like Windfree and so on. + # The following modes are available: sleep, quiet, fast, longreach, windfree + presets: + # short version + quiet: true + # long version - allows to locilize names + quiet: + name: "Makes no sound" + enabled: true + devices: # Skip everything below on the first run! Wait a minute, watch your ESPHome logs until you see the "Discovered devices:" section and you see some addresses: # For NASA devices it looks like this (with 4 indoor devices): @@ -63,7 +80,12 @@ samsung_ac: - address: "20.00.00" # Indoor device address # Each property below is optional (climate, room_temperature etc.) - you can delete those which you dont need. # For the names I suggest to choose a combination of room name and the thing it controls. - + + # Configures/overrides the capabilites for this devices. + # Look above for all options. + capabilities: + horizontal_swing: false # This device have no h swing. + # Creates climate control in Home Assistant. A climate control combines multiple of the controls below (like temperature, mode etc.) climate: name: "Kitchen climate" From 836874b5c94dff0b8ba5ca1af86f270e7168f301 Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 16:21:04 +0100 Subject: [PATCH 13/17] fix: get_supported_alt_modes should not be null. --- components/samsung_ac/samsung_ac_device.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index 3c8c791..08d3d39 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -105,11 +105,10 @@ namespace esphome outdoor_temperature = sensor; } - void add_custom_sensor(int message_number, sensor::Sensor *sensor) { Samsung_AC_Sensor cust_sensor; - cust_sensor.message_number = (uint16_t) message_number; + cust_sensor.message_number = (uint16_t)message_number; cust_sensor.sensor = sensor; custom_sensors.push_back(std::move(cust_sensor)); } @@ -117,7 +116,7 @@ namespace esphome std::set get_custom_sensors() { std::set numbers; - for (auto &sensor: custom_sensors) + for (auto &sensor : custom_sensors) numbers.insert(sensor.message_number); return numbers; } @@ -219,7 +218,8 @@ namespace esphome if (climate != nullptr) { auto supported = get_supported_alt_modes(); - auto mode = std::find_if(supported->begin(), supported->end(), [&value](const AltModeDesc& x) { return x.value == value; }); + auto mode = std::find_if(supported->begin(), supported->end(), [&value](const AltModeDesc &x) + { return x.value == value; }); if (mode == supported->end()) { ESP_LOGW(TAG, "Unsupported alt_mode %d", value); @@ -278,7 +278,7 @@ namespace esphome void update_custom_sensor(uint16_t message_number, float value) { - for (auto &sensor: custom_sensors) + for (auto &sensor : custom_sensors) if (sensor.message_number == message_number) sensor.sensor->publish_state(value); } @@ -302,7 +302,7 @@ namespace esphome { supports_horizontal_swing_ = value; } - + void set_supports_vertical_swing(bool value) { supports_vertical_swing_ = value; @@ -318,9 +318,7 @@ namespace esphome const std::vector *get_supported_alt_modes() { - if (!alt_modes.empty()) - return &alt_modes; - return nullptr; + return &alt_modes; } protected: From c708c7630bde8a596e07a4776e6ea3728992a7af Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 16:22:02 +0100 Subject: [PATCH 14/17] fix: get_supported_alt_modes should not be null. --- components/samsung_ac/samsung_ac_device.cpp | 17 +++++++++++------ components/samsung_ac/samsung_ac_device.h | 16 +++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/components/samsung_ac/samsung_ac_device.cpp b/components/samsung_ac/samsung_ac_device.cpp index b3803a3..3a73015 100644 --- a/components/samsung_ac/samsung_ac_device.cpp +++ b/components/samsung_ac/samsung_ac_device.cpp @@ -51,11 +51,12 @@ namespace esphome traits.set_supported_custom_fan_modes(customFan); auto supported = device->get_supported_alt_modes(); - if (supported != nullptr && !supported->empty()) + if (!supported->empty()) { std::set presets; std::set custom_presets; - for (const AltModeDesc& mode: *supported) { + for (const AltModeDesc &mode : *supported) + { auto preset = altmodename_to_preset(mode.name); if (preset) presets.insert(preset.value()); @@ -72,9 +73,12 @@ namespace esphome { std::set swingMode; swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_OFF); - if (h) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_HORIZONTAL); - if (v) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_VERTICAL); - if (h && v) swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_BOTH); + if (h) + swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_HORIZONTAL); + if (v) + swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_VERTICAL); + if (h && v) + swingMode.insert(climate::ClimateSwingMode::CLIMATE_SWING_BOTH); traits.set_supported_swing_modes(swingMode); } @@ -140,7 +144,8 @@ namespace esphome void Samsung_AC_Climate::set_alt_mode_by_name(ProtocolRequest &request, const AltModeName &name) { auto supported = device->get_supported_alt_modes(); - auto mode = std::find_if(supported->begin(), supported->end(), [&name](const AltModeDesc &x) { return x.name == name;}); + auto mode = std::find_if(supported->begin(), supported->end(), [&name](const AltModeDesc &x) + { return x.name == name; }); if (mode == supported->end()) { ESP_LOGW(TAG, "Unsupported alt_mode %s", name); diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index 3c8c791..08d3d39 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -105,11 +105,10 @@ namespace esphome outdoor_temperature = sensor; } - void add_custom_sensor(int message_number, sensor::Sensor *sensor) { Samsung_AC_Sensor cust_sensor; - cust_sensor.message_number = (uint16_t) message_number; + cust_sensor.message_number = (uint16_t)message_number; cust_sensor.sensor = sensor; custom_sensors.push_back(std::move(cust_sensor)); } @@ -117,7 +116,7 @@ namespace esphome std::set get_custom_sensors() { std::set numbers; - for (auto &sensor: custom_sensors) + for (auto &sensor : custom_sensors) numbers.insert(sensor.message_number); return numbers; } @@ -219,7 +218,8 @@ namespace esphome if (climate != nullptr) { auto supported = get_supported_alt_modes(); - auto mode = std::find_if(supported->begin(), supported->end(), [&value](const AltModeDesc& x) { return x.value == value; }); + auto mode = std::find_if(supported->begin(), supported->end(), [&value](const AltModeDesc &x) + { return x.value == value; }); if (mode == supported->end()) { ESP_LOGW(TAG, "Unsupported alt_mode %d", value); @@ -278,7 +278,7 @@ namespace esphome void update_custom_sensor(uint16_t message_number, float value) { - for (auto &sensor: custom_sensors) + for (auto &sensor : custom_sensors) if (sensor.message_number == message_number) sensor.sensor->publish_state(value); } @@ -302,7 +302,7 @@ namespace esphome { supports_horizontal_swing_ = value; } - + void set_supports_vertical_swing(bool value) { supports_vertical_swing_ = value; @@ -318,9 +318,7 @@ namespace esphome const std::vector *get_supported_alt_modes() { - if (!alt_modes.empty()) - return &alt_modes; - return nullptr; + return &alt_modes; } protected: From 6d35cf1490e8ff534c467658dcb64f2bcc4ca05f Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Mon, 19 Feb 2024 16:36:27 +0100 Subject: [PATCH 15/17] Add room_temperature_offset --- components/samsung_ac/__init__.py | 7 ++++++- components/samsung_ac/samsung_ac_device.h | 10 ++++++++-- example.yaml | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/components/samsung_ac/__init__.py b/components/samsung_ac/__init__.py index b4e5d17..7b8a791 100644 --- a/components/samsung_ac/__init__.py +++ b/components/samsung_ac/__init__.py @@ -51,6 +51,7 @@ CONF_DEVICE_ID = "samsung_ac_device_id" CONF_DEVICE_ADDRESS = "address" CONF_DEVICE_ROOM_TEMPERATURE = "room_temperature" +CONF_DEVICE_ROOM_TEMPERATURE_OFFSET = "room_temperature_offset" CONF_DEVICE_TARGET_TEMPERATURE = "target_temperature" CONF_DEVICE_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_DEVICE_POWER = "power" @@ -168,13 +169,13 @@ def humidity_sensor_schema(message: int): device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_DEVICE_ROOM_TEMPERATURE_OFFSET): cv.float_, cv.Optional(CONF_DEVICE_OUTDOOR_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_DEVICE_TARGET_TEMPERATURE): NUMBER_SCHEMA, cv.Optional(CONF_DEVICE_POWER): switch.switch_schema(Samsung_AC_Switch), cv.Optional(CONF_DEVICE_MODE): SELECT_MODE_SCHEMA, @@ -290,6 +291,10 @@ async def to_code(config): sens = await sensor.new_sensor(conf) cg.add(var_dev.set_room_temperature_sensor(sens)) + if CONF_DEVICE_ROOM_TEMPERATURE_OFFSET in device: + cg.add(var_dev.set_room_temperature_offset( + device[CONF_DEVICE_ROOM_TEMPERATURE_OFFSET])) + if CONF_DEVICE_OUTDOOR_TEMPERATURE in device: conf = device[CONF_DEVICE_OUTDOOR_TEMPERATURE] sens = await sensor.new_sensor(conf) diff --git a/components/samsung_ac/samsung_ac_device.h b/components/samsung_ac/samsung_ac_device.h index 08d3d39..bd1717d 100644 --- a/components/samsung_ac/samsung_ac_device.h +++ b/components/samsung_ac/samsung_ac_device.h @@ -94,6 +94,7 @@ namespace esphome Samsung_AC_Mode_Select *mode{nullptr}; Samsung_AC_Climate *climate{nullptr}; std::vector custom_sensors; + float room_temperature_offset{0}; void set_room_temperature_sensor(sensor::Sensor *sensor) { @@ -262,10 +263,10 @@ namespace esphome void update_room_temperature(float value) { if (room_temperature != nullptr) - room_temperature->publish_state(value); + room_temperature->publish_state(value + room_temperature_offset); if (climate != nullptr) { - climate->current_temperature = value; + climate->current_temperature = value + room_temperature_offset; climate->publish_state(); } } @@ -321,6 +322,11 @@ namespace esphome return &alt_modes; } + void set_room_temperature_offset(float value) + { + room_temperature_offset = value; + } + protected: bool supports_horizontal_swing_{true}; bool supports_vertical_swing_{true}; diff --git a/example.yaml b/example.yaml index b0b57e6..a9306eb 100644 --- a/example.yaml +++ b/example.yaml @@ -79,7 +79,7 @@ samsung_ac: # Repeat everything below for each indoor device address you see in your logs - address: "20.00.00" # Indoor device address # Each property below is optional (climate, room_temperature etc.) - you can delete those which you dont need. - # For the names I suggest to choose a combination of room name and the thing it controls. + # For the names we suggest to choose a combination of room name and the thing it controls. # Configures/overrides the capabilites for this devices. # Look above for all options. @@ -100,6 +100,10 @@ samsung_ac: mode: name: "Kitchen mode" + # If your AC sits near or inside the ceiling, the reported room temperature is often a little bit heigher then whats + # measured below. This property can be used to correct that value. + room_temperature_offset: -1.4 + # Only supported on NASA devices room_humidity: name: "Kitchen humidity" From c773e97cf7c82ff8496cc17d4ca75be1ff9f7d5d Mon Sep 17 00:00:00 2001 From: Steve Wagner Date: Fri, 23 Feb 2024 07:57:55 +0100 Subject: [PATCH 16/17] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a4fbf51..e334ffb 100644 --- a/readme.md +++ b/readme.md @@ -30,7 +30,7 @@ Its cheap, comes with a tiny case (witch can fit inside an indoor unit) and allo 1. Purchase the following components and stack them: - **M5STACK ATOM Lite** - [Aliexpress](https://a.aliexpress.com/_mO88aeK), [M5STACK store](https://shop.m5stack.com/products/atom-lite-esp32-development-kit), [documentation](https://docs.m5stack.com/en/core/ATOM%20Lite) - - **M5STACK ATOM RS-485 Kit** - [Aliexpress](https://a.aliexpress.com/_mLhOZQA), [M5STACK store](https://shop.m5stack.com/products/atom-rs485-kit?variant=34787900194980), [documentation](https://docs.m5stack.com/en/atom/atomic485) + - **M5STACK ATOM RS-485 Base** - [Aliexpress](https://a.aliexpress.com/_mLhOZQA), [M5STACK store](https://shop.m5stack.com/products/atomic-rs485-base), [documentation](https://docs.m5stack.com/en/atom/atomic485) 1. Connect the components as follows: - Connect F1 on the AC unit to A on the M5STACK controller. From e61cd23f900d993caf9b91d7a6051cf0bccd3d45 Mon Sep 17 00:00:00 2001 From: nickcox <135552+nickcox@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:51:16 +1100 Subject: [PATCH 17/17] Fix minor typos in readme --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index e334ffb..44ec43e 100644 --- a/readme.md +++ b/readme.md @@ -26,7 +26,7 @@ The current implementation offers the following features: An ESPHome compatible device and an RS-485 to (TTL) serial adapter is required to run this project. While its possible to run it on an ESP8266 its better to chose an ESP32 since it handles the incoming message stream better (cause it has more CPU power and RAM). ### We recommand to use the M5STACK ATOM Lite + M5STACK RS-485 kit -Its cheap, comes with a tiny case (witch can fit inside an indoor unit) and allow directly to use the 12V comming from the V1/V2 lines witch some AC units provide. +Its cheap, comes with a tiny case (which can fit inside an indoor unit) and allow directly to use the 12V comming from the V1/V2 lines which some AC units provide. 1. Purchase the following components and stack them: - **M5STACK ATOM Lite** - [Aliexpress](https://a.aliexpress.com/_mO88aeK), [M5STACK store](https://shop.m5stack.com/products/atom-lite-esp32-development-kit), [documentation](https://docs.m5stack.com/en/core/ATOM%20Lite) @@ -45,7 +45,7 @@ Its cheap, comes with a tiny case (witch can fit inside an indoor unit) and allo Follow these steps to install and configure the software for your AC unit controller: 1. **Create a New ESPHome Device:** - - Begin by creating a new ESPHome device in your Home Assistant instance or ESPHome comand line tool. + - Begin by creating a new ESPHome device in your Home Assistant instance or ESPHome command line tool. - Use the configuration from [example.yaml](https://github.com/lanwin/esphome_samsung_ac/blob/main/example.yaml) file as a template and copy over the `api` and `ota` sections from the newly created YAML. 1. **Deploy and Boot:** @@ -76,7 +76,7 @@ Follow these steps to install and configure the software for your AC unit contro * Test if swapping F1/F2 helps * Change **baud_rate** from 9600 to 2400 (some older hardware uses a lower baud rate) * For some boards (like NodeMCU) you need to disable serial logging, since it blocks the pins required for the RS484 serial communication. Just add `baud_rate: 0` to the logger section. -* Add the following to your yaml witch dumps all data witch is received via RS484 to logs. This helps to check if you get any data. This also helps when reporting problems. +* Add the following to your yaml which dumps all data which is received via RS484 to logs. This helps to check if you get any data. This also helps when reporting problems. ```yaml debug: direction: BOTH @@ -121,8 +121,8 @@ newer NASA protocol is more complex and allows more data to be transferred and m ### NASA -The NASA protocol is pretty generic. Its basicaly desinged to transport variables witch are a key (number) and a value (with -an datatype like Enum,Int,Long,Bytes). All meaning is defined to the keys. If you want to know the room temperature you need +The NASA protocol is pretty generic. Its basicaly desinged to transport variables which are a key (number) and a value (with +a datatype like Enum, Int, Long, Bytes). All meaning is defined to the keys. If you want to know the room temperature you need to know the number and wait for it. [Foxhill67](https://github.com/Foxhill67) started to document the NASA protocol [here](https://wiki.myehs.eu/wiki/NASA_Protocol).