Skip to content

Commit

Permalink
Merge pull request #169 from omerfaruk-aran/feature/handle-value-changes
Browse files Browse the repository at this point in the history
Handle UI Update Issues - Improved Value Change Handling in Samsung AC Integration
  • Loading branch information
lanwin authored Oct 9, 2024
2 parents 8033246 + a17fc5a commit 1917619
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 199 deletions.
112 changes: 46 additions & 66 deletions components/samsung_ac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
CONF_DEVICE_ERROR_CODE = "error_code"



CONF_CAPABILITIES = "capabilities"
CONF_CAPABILITIES_HORIZONTAL_SWING = "horizontal_swing"
CONF_CAPABILITIES_VERTICAL_SWING = "vertical_swing"
Expand Down Expand Up @@ -280,41 +281,28 @@ async def to_code(config):
device[CONF_DEVICE_ID], device[CONF_DEVICE_ADDRESS], var)

# setup capabilities
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]))
capabilities = device.get(CONF_CAPABILITIES, config.get(CONF_CAPABILITIES, {}))

if capabilities.get(CONF_CAPABILITIES_VERTICAL_SWING):
cg.add(var_dev.set_supports_vertical_swing(True))

if capabilities.get(CONF_CAPABILITIES_HORIZONTAL_SWING):
cg.add(var_dev.set_supports_horizontal_swing(True))

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
presets = capabilities.get(CONF_PRESETS, {})

for preset, preset_info in PRESETS.items():
preset_conf = presets.get(preset, None)
preset_dict = isinstance(preset_conf, dict)
if preset_conf == True or (preset_dict and preset_conf[CONF_PRESET_ENABLED] == True):
if preset_conf == True or (preset_dict and preset_conf.get(CONF_PRESET_ENABLED, False)):
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"]
preset_conf.get(CONF_PRESET_NAME, preset_info["displayName"]),
preset_conf.get(CONF_PRESET_VALUE, preset_info["value"])
))

# if CONF_CAPABILITIES in device and CONF_ALT_MODES in device[CONF_CAPABILITIES]:
Expand All @@ -326,49 +314,28 @@ async def to_code(config):
# 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)
cg.add(var_dev.set_power_switch(sens))

if CONF_DEVICE_AUTOMATIC_CLEANING in device:
conf = device[CONF_DEVICE_AUTOMATIC_CLEANING]
sens = await switch.new_switch(conf)
cg.add(var_dev.set_automatic_cleaning_switch(sens))

if CONF_DEVICE_WATER_HEATER_POWER in device:
conf = device[CONF_DEVICE_WATER_HEATER_POWER]
sens = await switch.new_switch(conf)
cg.add(var_dev.set_water_heater_power_switch(sens))
# Mapping of config keys to their corresponding methods and types
device_actions = {
CONF_DEVICE_POWER: (switch.new_switch, var_dev.set_power_switch),
CONF_DEVICE_AUTOMATIC_CLEANING: (switch.new_switch, var_dev.set_automatic_cleaning_switch),
CONF_DEVICE_WATER_HEATER_POWER: (switch.new_switch, var_dev.set_water_heater_power_switch),
CONF_DEVICE_ROOM_TEMPERATURE: (sensor.new_sensor, var_dev.set_room_temperature_sensor),
CONF_DEVICE_OUTDOOR_TEMPERATURE: (sensor.new_sensor, var_dev.set_outdoor_temperature_sensor),
CONF_DEVICE_INDOOR_EVA_IN_TEMPERATURE: (sensor.new_sensor, var_dev.set_indoor_eva_in_temperature_sensor),
CONF_DEVICE_INDOOR_EVA_OUT_TEMPERATURE: (sensor.new_sensor, var_dev.set_indoor_eva_out_temperature_sensor),
CONF_DEVICE_ERROR_CODE: (sensor.new_sensor, var_dev.set_error_code_sensor),
}

if CONF_DEVICE_ROOM_TEMPERATURE in device:
conf = device[CONF_DEVICE_ROOM_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var_dev.set_room_temperature_sensor(sens))
# Iterate over the actions
for key, (action, method) in device_actions.items():
if key in device:
conf = device[key]
sens = await action(conf)
cg.add(method(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)
cg.add(var_dev.set_outdoor_temperature_sensor(sens))

if CONF_DEVICE_INDOOR_EVA_IN_TEMPERATURE in device:
conf = device[CONF_DEVICE_INDOOR_EVA_IN_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var_dev.set_indoor_eva_in_temperature_sensor(sens))

if CONF_DEVICE_INDOOR_EVA_OUT_TEMPERATURE in device:
conf = device[CONF_DEVICE_INDOOR_EVA_OUT_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var_dev.set_indoor_eva_out_temperature_sensor(sens))

if CONF_DEVICE_ERROR_CODE in device:
conf = device[CONF_DEVICE_ERROR_CODE]
sens = await sensor.new_sensor(conf)
cg.add(var_dev.set_error_code_sensor(sens))

if CONF_DEVICE_WATER_TARGET_TEMPERATURE in device:
conf = device[CONF_DEVICE_WATER_TARGET_TEMPERATURE]
Expand Down Expand Up @@ -452,6 +419,19 @@ async def to_code(config):

if (CONF_DEBUG_LOG_UNDEFINED_MESSAGES in config):
cg.add(var.set_debug_log_undefined_messages(config[CONF_DEBUG_LOG_UNDEFINED_MESSAGES]))


# Mapping of config keys to their corresponding methods
config_actions = {
CONF_DEBUG_LOG_MESSAGES: var.set_debug_log_messages,
CONF_DEBUG_LOG_MESSAGES_RAW: var.set_debug_log_messages_raw,
CONF_NON_NASA_KEEPALIVE: var.set_non_nasa_keepalive,
CONF_DEBUG_LOG_UNDEFINED_MESSAGES: var.set_debug_log_undefined_messages,
}

# Iterate over the actions
for key, method in config_actions.items():
if key in config:
cg.add(method(config[key]))

await cg.register_component(var, config)
await uart.register_uart_device(var, config)
4 changes: 2 additions & 2 deletions components/samsung_ac/conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace esphome
case Mode::Heat:
return "Heat";
default:
return "";
return "Unknown";
};
}

Expand Down Expand Up @@ -64,7 +64,7 @@ namespace esphome
case WaterHeaterMode::Force:
return "Force";
default:
return "";
return "Unknown";
};
}

Expand Down
34 changes: 12 additions & 22 deletions components/samsung_ac/debug_mqtt.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#include "esphome/core/log.h"
#include "debug_mqtt.h"

#ifdef USE_ESP8266
#if defined(USE_ESP8266)
#include <AsyncMqttClient.h>
AsyncMqttClient *mqtt_client{nullptr};
#endif
#ifdef USE_ESP32
#elif defined(USE_ESP32)
#include <mqtt_client.h>
esp_mqtt_client_handle_t mqtt_client{nullptr};
#endif
Expand All @@ -16,44 +15,40 @@ namespace esphome
{
bool debug_mqtt_connected()
{
#ifdef USE_ESP8266
if (mqtt_client == nullptr)
return false;

#if defined(USE_ESP8266)
return mqtt_client->connected();
#elif USE_ESP32
if (mqtt_client == nullptr)
return false;

#elif defined(USE_ESP32)
return true;
#else
return false;
#endif
}

void debug_mqtt_connect(const std::string &host, const uint16_t port, const std::string &username, const std::string &password)
{
if (host.length() == 0)
if (host.empty())
return;

#ifdef USE_ESP8266
#if defined(USE_ESP8266)
if (mqtt_client == nullptr)
{
mqtt_client = new AsyncMqttClient();
mqtt_client->setServer(host.c_str(), port);
if (username.length() > 0)
if (!username.empty())
mqtt_client->setCredentials(username.c_str(), password.c_str());
}

if (!mqtt_client->connected())
mqtt_client->connect();
#elif USE_ESP32

#elif defined(USE_ESP32)
if (mqtt_client == nullptr)
{
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.host = host.c_str();
mqtt_cfg.port = port;
if (username.length() > 0)
if (!username.empty())
{
mqtt_cfg.username = username.c_str();
mqtt_cfg.password = password.c_str();
Expand All @@ -66,18 +61,13 @@ namespace esphome

bool debug_mqtt_publish(const std::string &topic, const std::string &payload)
{
#ifdef USE_ESP8266
if (mqtt_client == nullptr)
return false;

#if defined(USE_ESP8266)
return mqtt_client->publish(topic.c_str(), 0, false, payload.c_str()) != 0;
#elif USE_ESP32
if (mqtt_client == nullptr)
return false;

#elif defined(USE_ESP32)
return esp_mqtt_client_publish(mqtt_client, topic.c_str(), payload.c_str(), payload.length(), 0, false) != -1;
#else
return true;
#endif
}
} // namespace samsung_ac
Expand Down
3 changes: 2 additions & 1 deletion components/samsung_ac/debug_mqtt.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <iostream>

#include <string>

namespace esphome
{
Expand Down
56 changes: 56 additions & 0 deletions components/samsung_ac/device_state_tracker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef DEVICE_STATE_TRACKER_H
#define DEVICE_STATE_TRACKER_H

#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include <map>
#include <string>

namespace esphome {

template <typename T>
class DeviceStateTracker {
public:
DeviceStateTracker(unsigned long timeout_period)
: TIMEOUT_PERIOD(timeout_period) {}

void update(const std::string &address, const T &current_value) {
unsigned long now = millis();

if (pending_changes_.find(address) != pending_changes_.end()) {
if (current_value == pending_changes_[address]) {
pending_changes_.erase(address);
} else {
ESP_LOGI("device_state_tracker", "Stale value received for device: %s, ignoring.", address.c_str());
return;
}
}

if (last_values_.find(address) == last_values_.end() || last_values_[address] != current_value) {
pending_changes_[address] = current_value;
last_values_[address] = current_value;
last_update_time_[address] = now;

ESP_LOGI("device_state_tracker", "Value changed for device: %s", address.c_str());
} else {
ESP_LOGD("device_state_tracker", "No change in value for device: %s", address.c_str());

if (now - last_update_time_[address] > TIMEOUT_PERIOD) {
if (pending_changes_.find(address) != pending_changes_.end()) {
ESP_LOGW("device_state_tracker", "Timeout for device: %s, forcing update.", address.c_str());
pending_changes_.erase(address);
}
}
}
}

private:
std::map<std::string, T> last_values_;
std::map<std::string, unsigned long> last_update_time_;
std::map<std::string, T> pending_changes_;
const unsigned long TIMEOUT_PERIOD;
};

} // namespace esphome

#endif // DEVICE_STATE_TRACKER_H
29 changes: 6 additions & 23 deletions components/samsung_ac/protocol_nasa.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#include <queue>
#include <iostream>
#include <set>
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/core/hal.h"
#include "util.h"
#include "protocol_nasa.h"
#include "debug_mqtt.h"
Expand All @@ -21,9 +18,9 @@ namespace esphome
return value - (int)65535 /*uint16 max*/ - 1.0;
}

#define LOG_MESSAGE(message_name, temp, source, dest) \
if (debug_log_messages) \
{ \
#define LOG_MESSAGE(message_name, temp, source, dest) \
if (debug_log_messages) \
{ \
ESP_LOGW(TAG, "s:%s d:%s " #message_name " %g", source.c_str(), dest.c_str(), static_cast<double>(temp)); \
}

Expand Down Expand Up @@ -82,8 +79,8 @@ namespace esphome
std::string Address::to_string()
{
char str[9];
sprintf(str, "%02x.%02x.%02x", (int)klass, channel, address);
return str;
sprintf(str, "%02x.%02x.%02x", klass, channel, address);
return std::string(str);
}

void Command::decode(std::vector<uint8_t> &data, unsigned int index)
Expand Down Expand Up @@ -861,11 +858,9 @@ namespace esphome

void process_messageset_debug(std::string source, std::string dest, MessageSet &message, MessageTarget *target)
{
if (source == "20.00.00" || source == "20.00.01" || source == "20.00.03")
if (source == "20.00.00" || source == "20.00.01" || source == "20.00.02" || source == "20.00.03")
return;

// return; // :)

switch ((uint16_t)message.messageNumber)
{
case 0x4003:
Expand Down Expand Up @@ -900,18 +895,6 @@ namespace esphome
LOG_MESSAGE(ENUM_in_fan_vent_mode, message.value, source, dest);
// fan_vent_mode_to_fanmode();
break;
case 0x4205: // VAR_in_temp_eva_in_f unit = 'Celsius'
{
double temp = (double)message.value / (double)10;
LOG_MESSAGE(VAR_in_temp_eva_in_f, temp, source, dest);
break;
}
case 0x4206: // VAR_in_temp_eva_out_f unit = 'Celsius'
{
double temp = (double)message.value / (double)10;
LOG_MESSAGE(VAR_in_temp_eva_out_f, temp, source, dest);
break;
}
case 0x4211: // VAR_in_capacity_request unit = 'kW'
{
double temp = (double)message.value / (double)8.6;
Expand Down
1 change: 0 additions & 1 deletion components/samsung_ac/protocol_nasa.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include <vector>
#include <iostream>
#include "protocol.h"

namespace esphome
Expand Down
Loading

0 comments on commit 1917619

Please sign in to comment.