From c2d5f8f011d9641ca5c6053a2b49cf7e24a2a770 Mon Sep 17 00:00:00 2001
From: sqozz <sqozz-git@geekify.de>
Date: Thu, 19 Sep 2024 22:44:44 +0200
Subject: [PATCH] Add all documented selects

---
 __init__.py              |  2 +-
 select/__init__.py       | 49 +++++++++++++-----------
 select/tw7100_select.cpp | 32 ++++++++++++++--
 select/tw7100_select.h   | 65 ++++++++++++++++++++++++++++++-
 tw7100.cpp               | 82 ++++++++++++++++++++++++++--------------
 tw7100.h                 | 24 +++++++++++-
 6 files changed, 196 insertions(+), 58 deletions(-)

diff --git a/__init__.py b/__init__.py
index a8553f3..d1eb51f 100644
--- a/__init__.py
+++ b/__init__.py
@@ -6,7 +6,7 @@ from esphome.const import CONF_ID
 
 CODEOWNERS = ["@sqozz"]
 DEPENDENCIES = ["uart"]
-AUTO_LOAD = ["binary_sensor", "sensor", "text_sensor"]
+AUTO_LOAD = ["binary_sensor", "sensor", "text_sensor", "number", "switch"]
 
 tw7100_ns = cg.esphome_ns.namespace("tw7100")
 tw7100 = tw7100_ns.class_("tw7100Component", cg.PollingComponent, uart.UARTDevice)
diff --git a/select/__init__.py b/select/__init__.py
index 5423bfc..9ead2bb 100644
--- a/select/__init__.py
+++ b/select/__init__.py
@@ -10,30 +10,35 @@ CODEOWNERS = ["@sqozz"]
 
 tw7100Select = tw7100_ns.class_("tw7100Select", select.Select, cg.Component)
 
-CONFIG_SCHEMA = cv.Schema(
-    {
-        cv.GenerateID(): cv.use_id(tw7100),
-        cv.Optional("SOURCE"): select.select_schema(
-            tw7100Select
-        ),
-        cv.Optional("LUMINANCE"): select.select_schema(
-            tw7100Select
-        ),
-    }
-)
+selects = [
+  ("SOURCE", "mdi:video-input-hdmi"),
+  ("LUMINANCE", "mdi:brightness-6"),
+  ("CMODE", ""),
+  ("GAMMA", "mdi:gamma"),
+  ("IMGPRESET", "mdi:palette"),
+  ("MCFI", "mdi:image-multiple"),
+  ("CLRSPACE", ""),
+  ("DYNRANGE", "mdi:hdr"),
+  ("IMGPROC", ""),
+  ("MSEL", "mdi:image-off"),
+  ("SPEED", "mdi:speedometer"),
+  ("OVSCAN", "mdi:overscan"),
+  ("ASPECT", "mdi:aspect-ratio")
+]
+
+schema={cv.GenerateID(): cv.use_id(tw7100)}
+for name,icon in selects:
+    schema.update({cv.Optional(name): select.select_schema(tw7100Select, icon=icon)})
+
+CONFIG_SCHEMA = cv.Schema(schema)
 
 
 async def to_code(config):
     parent = await cg.get_variable(config[CONF_ID])
 
-    if "SOURCE" in config:
-        select_ = await select.new_select(config["SOURCE"], options=[])
-        cg.add(select_.set_cmd("SOURCE"))
-        cg.add(select_.set_tw7100_parent(parent))
-        cg.add(parent.set_set_source(select_))
-
-    if "LUMINANCE" in config:
-        select_ = await select.new_select(config["LUMINANCE"], options=[])
-        cg.add(select_.set_cmd("LUMINANCE"))
-        cg.add(select_.set_tw7100_parent(parent))
-        cg.add(parent.set_set_luminance(select_))
+    for name,icon in selects:
+        if name in config:
+            select_ = await select.new_select(config[name], options=[])
+            cg.add(select_.set_cmd(name))
+            cg.add(select_.set_tw7100_parent(parent))
+            cg.add(getattr(parent, "set_set_{}".format(name.lower()))(select_))
diff --git a/select/tw7100_select.cpp b/select/tw7100_select.cpp
index f197b9d..49413e2 100644
--- a/select/tw7100_select.cpp
+++ b/select/tw7100_select.cpp
@@ -25,6 +25,26 @@ void tw7100Select::setup() {
     traits.set_options(get_map_values(sources));
   } else if (cmd_ == "LUMINANCE") {
     traits.set_options(get_map_values(luminance));
+  } else if (cmd_ == "ASPECT") {
+    traits.set_options(get_map_values(aspect));
+  } else if (cmd_ == "OVSCAN") {
+    traits.set_options(get_map_values(ovscan));
+  } else if (cmd_ == "CMODE") {
+    traits.set_options(get_map_values(cmode));
+  } else if (cmd_ == "GAMMA") {
+    traits.set_options(get_map_values(gamma));
+  } else if (cmd_ == "IMGPRESET") {
+    traits.set_options(get_map_values(imgpreset));
+  } else if (cmd_ == "MCFI") {
+    traits.set_options(get_map_values(mcfi));
+  } else if (cmd_ == "CLRSPACE") {
+    traits.set_options(get_map_values(clrspace));
+  } else if (cmd_ == "DYNRANGE") {
+    traits.set_options(get_map_values(dynrange));
+  } else if (cmd_ == "MSEL") {
+    traits.set_options(get_map_values(msel));
+  } else if (cmd_ == "SPEED") {
+    traits.set_options(get_map_values(speed));
   }
   ESP_LOGV(TAG, "setup done");
 }
@@ -43,14 +63,18 @@ void tw7100Select::control(const std::string &value) {
     for (auto element : sources) { if (element.second == value) { param = element.first; break; } }
   } else if (cmd_ == "LUMINANCE") {
     for (auto element : luminance) { if (element.second == value) { param = element.first; break; } }
+  } else if (cmd_ == "ASPECT") {
+    for (auto element : aspect) { if (element.second == value) { param = element.first; break; } }
   }
 
-  if (param.length() > 0 && state != value) {
-    parent_->push_cmd(cmd_, param);
-    ESP_LOGV("", "CMD: %s, PARAM: %s", cmd_.c_str(), param.c_str());
+  if (param.length() > 0) {
+    if (active_index() != index_of(value)) { // only send out commands if the value actually changed
+      parent_->push_cmd(cmd_, param);
+      ESP_LOGV("", "CMD: %s, PARAM: %s", cmd_.c_str(), param.c_str());
+    }
   }
+  publish_state(value); // but acknowledge everything
 
-  publish_state(value);
   ESP_LOGV(TAG, "control done");
 };
 
diff --git a/select/tw7100_select.h b/select/tw7100_select.h
index 8460223..80e2fbd 100644
--- a/select/tw7100_select.h
+++ b/select/tw7100_select.h
@@ -13,7 +13,6 @@ class tw7100Select : public select::Select, public Component {
   public:
     void setup() override;
     void dump_config() override;
-    void set_options(std::vector<std::string> options) { this->traits.set_options(options); }
     void set_tw7100_parent(tw7100Component *parent) { this->parent_ = parent; }
     void set_cmd(std::string cmd) { this->cmd_ = cmd; }
     std::map<std::string, std::string> sources = {
@@ -26,6 +25,70 @@ class tw7100Select : public select::Select, public Component {
       {"01", "Eco"},
       {"02", "Medium"}
     };
+    std::map<std::string, std::string> aspect = {
+      {"00", "Normal"},
+      {"30", "Auto"},
+      {"40", "Full"},
+      {"50", "Zoom"},
+      {"80", "Anamorphic"},
+      {"90", "Hsqueeze"}
+    };
+    std::map<std::string, std::string> msel = {
+      {"00", "Black"},
+      {"01", "Blue"},
+      {"02", "User logo"}
+    };
+    std::map<std::string, std::string> speed = {
+      {"00", "9600bps"},
+      {"01", "19200bps"},
+      {"02", "38400bps"},
+      {"03", "57600bps"}
+    };
+    std::map<std::string, std::string> mcfi = {
+      {"00", "Off"},
+      {"01", "Low"},
+      {"02", "Normal"},
+      {"03", "High"}
+    };
+    std::map<std::string, std::string> clrspace = {
+      {"00", "Auto"},
+      {"01", "BT.709"},
+      {"02", "BT.2020"}
+    };
+    std::map<std::string, std::string> dynrange = {
+      {"00", "Auto"},
+      {"01", "SDR"},
+      {"20", "HDR10"},
+      {"30", "HLG"}
+    };
+    std::map<std::string, std::string> imgpreset = {
+      {"00", "Off"},
+      {"01", "Setting 1"},
+      {"02", "Setting 2"},
+      {"03", "Setting 3"},
+      {"04", "Setting 4"},
+      {"05", "Setting 5"}
+    };
+    std::map<std::string, std::string> gamma = {
+      {"20", "Setting 2"},
+      {"21", "Setting 1"},
+      {"22", "Setting 0"},
+      {"23", "Setting -1"},
+      {"24", "Setting -2"},
+      {"F0", "Custom"}
+    };
+    std::map<std::string, std::string> cmode = {
+      {"06", "Dynamic"},
+      {"07", "Natural"},
+      {"0C", "Bright Cinema"},
+      {"15", "Cinema"}
+    };
+    std::map<std::string, std::string> ovscan = {
+      {"00", "Off"},
+      {"02", "4%"},
+      {"04", "8%"},
+      {"A0", "Auto"}
+    };
 
   protected:
     void control(const std::string &value) override;
diff --git a/tw7100.cpp b/tw7100.cpp
index b1bbdad..7c602cc 100644
--- a/tw7100.cpp
+++ b/tw7100.cpp
@@ -20,18 +20,17 @@ std::vector<std::string> pwr_on_query_cmds{
 // Projection screen adjustment settings
   "VKEYSTONE?",
   "HKEYSTONE?",
+  "ASPECT?",
+  "LUMINANCE?",
+  "OVSCAN?",
 /*
   "QC?",
   "CORRECTMET?",
-  "ASPECT?",
 */
-  "LUMINANCE?",
-/*
-  "OVSCAN?",
-*/
-//
+
 // Source/Input/Resolution settings
   "SOURCE?",
+
 // Image settings
   "BRIGHT?",
   "CONTRAST?",
@@ -39,9 +38,7 @@ std::vector<std::string> pwr_on_query_cmds{
   "TINT?",
   "CTEMP?",
   "FCOLOR?",
-/*
   "CMODE?",
-*/
   "NRS?",
   "MPEGNRS?",
   "OFFSETR?",
@@ -50,25 +47,19 @@ std::vector<std::string> pwr_on_query_cmds{
   "GAINR?",
   "GAING?",
   "GAINB?",
-/*
   "GAMMA?",
   "CSEL?",
-*/ 
   "4KENHANCE?",
-/*
   "IMGPRESET?",
-*/ 
   "SHRF?",
   "SHRS?",
   "DERANGE?",
   "DESTRENGTH?",
-/*
   "MCFI?",
   "CLRSPACE?",
   "DYNRANGE?",
   "HDRPQ?",
   "HDRHLG?",
-*/ 
   "IMGPROC?",
 
 // Sound settings
@@ -79,10 +70,8 @@ std::vector<std::string> pwr_on_query_cmds{
 // Environment settings
   "HREVERSE?",
   "VREVERSE?",
-/*
   "MSEL?",
   "SPEED?",
-*/
   "ILLUM?",
   "STANDBYCONF?",
 //  "PRODUCT?", // produces ERR
@@ -98,17 +87,27 @@ std::vector<std::string> pwr_on_query_cmds{
 
 // Information
   "SIGNAL?",
-//  "SOURCELIST?" // Parser present in git history but removed until esphome can update selects after api connections are established
-//  "SOURCELISTA?", // same as SOURCELIST
   "LOGTO?", // produces ERR
 //  "SNO?" // Pushed on demand if sensor value is empty (e.g. after boot-up)
+//  "SOURCELIST?" // Parser present in git history but removed until esphome can update selects after api connections are established
+//  "SOURCELISTA?", // same as SOURCELIST
 };
 
 void tw7100Component::setup() {
   static const char *const TAG = "setup()";
   ESP_LOGV(TAG, "SETUP");
-  source_select_->setup();
-  luminance_select_->setup();
+  if (source_select_ != nullptr) source_select_->setup();
+  if (luminance_select_ != nullptr) luminance_select_->setup();
+  if (aspect_select_ != nullptr) aspect_select_->setup();
+  if (cmode_select_ != nullptr) cmode_select_->setup();
+  if (gamma_select_ != nullptr) gamma_select_->setup();
+  if (imgpreset_select_ != nullptr) imgpreset_select_->setup();
+  if (mcfi_select_ != nullptr) mcfi_select_->setup();
+  if (ovscan_select_ != nullptr) ovscan_select_->setup();
+  if (clrspace_select_ != nullptr) clrspace_select_->setup();
+  if (dynrange_select_ != nullptr) dynrange_select_->setup();
+  if (msel_select_ != nullptr) msel_select_->setup();
+  if (speed_select_ != nullptr) speed_select_->setup();
 };
 
 void tw7100Component::update() {
@@ -233,6 +232,38 @@ void tw7100Component::update_sensor(std::pair<std::string, std::string> data) {
     auto call = luminance_select_->make_call();
     call.set_option(luminance_select_->luminance[value_string]);
     call.perform();
+  } else if (cmd == "SOURCE") {
+    ESP_LOGV(TAG, "updating source select");
+    auto call = source_select_->make_call();
+    call.set_option(source_select_->sources[value_string]);
+    call.perform();
+  } else if (cmd == "ASPECT") {
+    ESP_LOGV(TAG, "updating aspect select");
+    if(aspect_select_->aspect.find(value_string) != aspect_select_->aspect.end()) {
+      auto call = aspect_select_->make_call();
+      call.set_option(aspect_select_->aspect[value_string]);
+      call.perform();
+    } else {
+      ESP_LOGE(TAG, "Result %s not present in options", value_string);
+    }
+  } else if (cmd == "OVSCAN") {
+    ESP_LOGV(TAG, "updating ovscan select");
+  } else if (cmd == "CMODE") {
+    ESP_LOGV(TAG, "updating cmode select");
+  } else if (cmd == "GAMMA") {
+    ESP_LOGV(TAG, "updating gamma select");
+  } else if (cmd == "IMGPRESET") {
+    ESP_LOGV(TAG, "updating imgpreset select");
+  } else if (cmd == "MCFI") {
+    ESP_LOGV(TAG, "updating mcfi select");
+  } else if (cmd == "CLRSPACE") {
+    ESP_LOGV(TAG, "updating clrspace select");
+  } else if (cmd == "DYNRANGE") {
+    ESP_LOGV(TAG, "updating dynrange select");
+  } else if (cmd == "MSEL") {
+    ESP_LOGV(TAG, "updating msel select");
+  } else if (cmd == "SPEED") {
+    ESP_LOGV(TAG, "updating speed select");
   } else if (cmd == "SNO") {
     serial_->publish_state(value_string);
   } else if (cmd == "BTAUDIO") {
@@ -277,11 +308,6 @@ void tw7100Component::update_sensor(std::pair<std::string, std::string> data) {
     ESP_LOGV(TAG, "updating logto switch");
     int value = std::stoi(value_string);
     logto_switch_->publish_state(value == 1);
-  } else if (cmd == "SOURCE") {
-    ESP_LOGV(TAG, "updating source select");
-    auto call = source_select_->make_call();
-    call.set_option(source_select_->sources[value_string]);
-    call.perform();
   } else if (cmd == "MUTE") {
     ESP_LOGV(TAG, "updating mute switch with value %s", value_string.c_str());
     mute_switch_->publish_state(value_string == "ON");
@@ -300,7 +326,7 @@ void tw7100Component::update_sensor(std::pair<std::string, std::string> data) {
   } else if (cmd == "BRIGHT") {
     int value = std::stoi(value_string);
     ESP_LOGV(TAG, "updating brightness number with value %s", value_string.c_str());
-    brightness_number_->publish_state(value);
+    if (brightness_number_ != nullptr) brightness_number_->publish_state(value);
   } else if (cmd == "CONTRAST") {
     int value = std::stoi(value_string);
     ESP_LOGV(TAG, "updating contrast number with value %s", value_string.c_str());
@@ -325,9 +351,9 @@ void tw7100Component::update_sensor(std::pair<std::string, std::string> data) {
     int value = std::stoi(value_string);
     ESP_LOGV(TAG, "updating nrs number with value %s", value_string.c_str());
     nrs_number_->publish_state(value);
-  } else if (cmd == "MPRGNRS") {
+  } else if (cmd == "MPEGNRS") {
     int value = std::stoi(value_string);
-    ESP_LOGV(TAG, "updating mprgngs number with value %s", value_string.c_str());
+    ESP_LOGV(TAG, "updating mpegngs number with value %s", value_string.c_str());
     mpegnrs_number_->publish_state(value);
   } else if (cmd == "OFFSETR") {
     int value = std::stoi(value_string);
diff --git a/tw7100.h b/tw7100.h
index 7c35d64..b94455a 100644
--- a/tw7100.h
+++ b/tw7100.h
@@ -56,8 +56,18 @@ class tw7100Component : public PollingComponent, public uart::UARTDevice {
     void set_set_imgproc(switch_::Switch *switch_) { this->img_processing_switch_ = switch_; }
     void set_set_4kenhance(switch_::Switch *switch_) { this->_4kenhance_switch_ = switch_; }
     void set_set_logto(switch_::Switch *switch_) { this->logto_switch_ = switch_; }
-    void set_set_source(tw7100::tw7100Select *source_) { this->source_select_ = source_; }
-    void set_set_luminance(tw7100::tw7100Select *luminance_) { this->luminance_select_ = luminance_; }
+    void set_set_source(tw7100::tw7100Select *select_) { this->source_select_ = select_; }
+    void set_set_luminance(tw7100::tw7100Select *select_) { this->luminance_select_ = select_; }
+    void set_set_aspect(tw7100::tw7100Select *select_) { this->aspect_select_ = select_; }
+    void set_set_ovscan(tw7100::tw7100Select *select_) { this->ovscan_select_ = select_; }
+    void set_set_cmode(tw7100::tw7100Select *select_) { this->cmode_select_ = select_; }
+    void set_set_gamma(tw7100::tw7100Select *select_) { this->gamma_select_ = select_; }
+    void set_set_imgpreset(tw7100::tw7100Select *select_) { this->imgpreset_select_ = select_; }
+    void set_set_mcfi(tw7100::tw7100Select *select_) { this->mcfi_select_ = select_; }
+    void set_set_clrspace(tw7100::tw7100Select *select_) { this->clrspace_select_ = select_; }
+    void set_set_dynrange(tw7100::tw7100Select *select_) { this->dynrange_select_ = select_; }
+    void set_set_msel(tw7100::tw7100Select *select_) { this->msel_select_ = select_; }
+    void set_set_speed(tw7100::tw7100Select *select_) { this->speed_select_ = select_; }
     void set_set_vol(tw7100::tw7100Number *number_) { this->volume_number_ = number_; }
     void set_set_vkeystone(tw7100::tw7100Number *number_) { this->vkeystone_number_ = number_; }
     void set_set_hkeystone(tw7100::tw7100Number *number_) { this->hkeystone_number_ = number_; }
@@ -105,6 +115,16 @@ class tw7100Component : public PollingComponent, public uart::UARTDevice {
     switch_::Switch *logto_switch_{nullptr};
     tw7100::tw7100Select *source_select_{nullptr};
     tw7100::tw7100Select *luminance_select_{nullptr};
+    tw7100::tw7100Select *aspect_select_{nullptr};
+    tw7100::tw7100Select *ovscan_select_{nullptr};
+    tw7100::tw7100Select *cmode_select_{nullptr};
+    tw7100::tw7100Select *gamma_select_{nullptr};
+    tw7100::tw7100Select *imgpreset_select_{nullptr};
+    tw7100::tw7100Select *mcfi_select_{nullptr};
+    tw7100::tw7100Select *clrspace_select_{nullptr};
+    tw7100::tw7100Select *dynrange_select_{nullptr};
+    tw7100::tw7100Select *msel_select_{nullptr};
+    tw7100::tw7100Select *speed_select_{nullptr};
     tw7100::tw7100Number *volume_number_{nullptr};
     tw7100::tw7100Number *vkeystone_number_{nullptr};
     tw7100::tw7100Number *hkeystone_number_{nullptr};