Add new Location Indicator property

In ComputerSystem 1.13 and Chassis 1.14 as well as in many other
schemas IndicatorLED is replaced with LocationIndicatorActive.

LocationIndicatorActive is a bool while IndicatorLED had 3 states:
Lit, Blink, and off. Map Lit and Blink both to
LocationIndicatorActive true.

led.hpp was calling both enclosure_identify_blink and
enclosure_identify, continue this.

Keep the deprecated IndicatorLED and implement the new
LocationIndicatorActive property. Have both properties for
the time being. This new property makes the same calls.

This does add a new Redfish warning for the deprecated
IndicatorLED. Other warning are there today.

Tested: Validator passes
Could use help testing on Chassis. Our systems don't have a
IndicatorLED on chassis. See chassis bumped.
curl -k https://$bmc/redfish/v1/Systems/system
{
  "@odata.id": "/redfish/v1/Systems/system",
  "@odata.type": "#ComputerSystem.v1_13_0.ComputerSystem",
...
  "IndicatorLED": "Off",
  "LastResetTime": "2020-10-29T09:01:03+00:00",
  "Links": {
    "Chassis": [
      {
        "@odata.id": "/redfish/v1/Chassis/chassis"
      }
    ],
    "ManagedBy": [
      {
        "@odata.id": "/redfish/v1/Managers/bmc"
      }
    ]
  },
  "LocationIndicatorActive": false,

curl -X PATCH -d '{ "LocationIndicatorActive":true}' -k \
https://$bmc/redfish/v1/Systems/system

curl -X PATCH -d '{ "IndicatorLED":"Off"}' -k \
https://$bmc/redfish/v1/Systems/system

Change-Id: I105bed5794912c575aa9a00e0442461bfdee6180
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index b718698..cd26849 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -288,7 +288,7 @@
                     }
 
                     asyncResp->res.jsonValue["@odata.type"] =
-                        "#Chassis.v1_10_0.Chassis";
+                        "#Chassis.v1_14_0.Chassis";
                     asyncResp->res.jsonValue["@odata.id"] =
                         "/redfish/v1/Chassis/" + chassisId;
                     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
@@ -318,6 +318,7 @@
                                       interface) != interfaces2.end())
                         {
                             getIndicatorLedState(asyncResp);
+                            getLocationIndicatorActive(asyncResp);
                             break;
                         }
                     }
@@ -381,7 +382,7 @@
 
                 // Couldn't find an object with that name.  return an error
                 messages::resourceNotFound(
-                    asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
+                    asyncResp->res, "#Chassis.v1_14_0.Chassis", chassisId);
             },
             "xyz.openbmc_project.ObjectMapper",
             "/xyz/openbmc_project/object_mapper",
@@ -394,6 +395,7 @@
     void doPatch(crow::Response& res, const crow::Request& req,
                  const std::vector<std::string>& params) override
     {
+        std::optional<bool> locationIndicatorActive;
         std::optional<std::string> indicatorLed;
         auto asyncResp = std::make_shared<AsyncResp>(res);
 
@@ -402,12 +404,15 @@
             return;
         }
 
-        if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed))
+        if (!json_util::readJson(req, res, "LocationIndicatorActive",
+                                 locationIndicatorActive, "IndicatorLED",
+                                 indicatorLed))
         {
             return;
         }
 
-        if (!indicatorLed)
+        // TODO (Gunnar): Remove IndicatorLED after enough time has passed
+        if (!locationIndicatorActive && !indicatorLed)
         {
             return; // delete this when we support more patch properties
         }
@@ -419,7 +424,7 @@
         const std::string& chassisId = params[0];
 
         crow::connections::systemBus->async_method_call(
-            [asyncResp, chassisId, indicatorLed](
+            [asyncResp, chassisId, locationIndicatorActive, indicatorLed](
                 const boost::system::error_code ec,
                 const crow::openbmc_mapper::GetSubTreeType& subtree) {
                 if (ec)
@@ -454,23 +459,35 @@
                     const std::vector<std::string>& interfaces3 =
                         connectionNames[0].second;
 
+                    const std::array<const char*, 2> hasIndicatorLed = {
+                        "xyz.openbmc_project.Inventory.Item.Panel",
+                        "xyz.openbmc_project.Inventory.Item.Board."
+                        "Motherboard"};
+                    bool indicatorChassis = false;
+                    for (const char* interface : hasIndicatorLed)
+                    {
+                        if (std::find(interfaces3.begin(), interfaces3.end(),
+                                      interface) != interfaces3.end())
+                        {
+                            indicatorChassis = true;
+                            break;
+                        }
+                    }
+                    if (locationIndicatorActive)
+                    {
+                        if (indicatorChassis)
+                        {
+                            setLocationIndicatorActive(
+                                asyncResp, *locationIndicatorActive);
+                        }
+                        else
+                        {
+                            messages::propertyUnknown(
+                                asyncResp->res, "LocationIndicatorActive");
+                        }
+                    }
                     if (indicatorLed)
                     {
-                        const std::array<const char*, 2> hasIndicatorLed = {
-                            "xyz.openbmc_project.Inventory.Item.Panel",
-                            "xyz.openbmc_project.Inventory.Item.Board."
-                            "Motherboard"};
-                        bool indicatorChassis = false;
-                        for (const char* interface : hasIndicatorLed)
-                        {
-                            if (std::find(interfaces3.begin(),
-                                          interfaces3.end(),
-                                          interface) != interfaces3.end())
-                            {
-                                indicatorChassis = true;
-                                break;
-                            }
-                        }
                         if (indicatorChassis)
                         {
                             setIndicatorLedState(asyncResp, *indicatorLed);
@@ -485,7 +502,7 @@
                 }
 
                 messages::resourceNotFound(
-                    asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
+                    asyncResp->res, "#Chassis.v1_14_0.Chassis", chassisId);
             },
             "xyz.openbmc_project.ObjectMapper",
             "/xyz/openbmc_project/object_mapper",
diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp
index 0cb522f..0fd1c9b 100644
--- a/redfish-core/lib/led.hpp
+++ b/redfish-core/lib/led.hpp
@@ -30,6 +30,7 @@
  *
  * @return None.
  */
+// TODO (Gunnar): Remove IndicatorLED after enough time has passed
 inline void getIndicatorLedState(const std::shared_ptr<AsyncResp>& aResp)
 {
     BMCWEB_LOG_DEBUG << "Get led groups";
@@ -98,6 +99,7 @@
  *
  * @return None.
  */
+// TODO (Gunnar): Remove IndicatorLED after enough time has passed
 inline void setIndicatorLedState(const std::shared_ptr<AsyncResp>& aResp,
                                  const std::string& ledState)
 {
@@ -152,4 +154,116 @@
         "xyz.openbmc_project.Led.Group", "Asserted",
         std::variant<bool>(ledBlinkng));
 }
+
+/**
+ * @brief Retrieves identify led group properties over dbus
+ *
+ * @param[in] aResp     Shared pointer for generating response message.
+ *
+ * @return None.
+ */
+inline void getLocationIndicatorActive(const std::shared_ptr<AsyncResp>& aResp)
+{
+    BMCWEB_LOG_DEBUG << "Get LocationIndicatorActive";
+    crow::connections::systemBus->async_method_call(
+        [aResp](const boost::system::error_code ec,
+                const std::variant<bool> asserted) {
+            // Some systems may not have enclosure_identify_blink object so
+            // proceed to get enclosure_identify state.
+            if (!ec)
+            {
+                const bool* blinking = std::get_if<bool>(&asserted);
+                if (!blinking)
+                {
+                    BMCWEB_LOG_DEBUG << "Get identity blinking LED failed";
+                    messages::internalError(aResp->res);
+                    return;
+                }
+                // Blinking ON, no need to check enclosure_identify assert.
+                if (*blinking)
+                {
+                    aResp->res.jsonValue["LocationIndicatorActive"] = true;
+                    return;
+                }
+            }
+            crow::connections::systemBus->async_method_call(
+                [aResp](const boost::system::error_code ec2,
+                        const std::variant<bool> asserted2) {
+                    if (!ec2)
+                    {
+                        const bool* ledOn = std::get_if<bool>(&asserted2);
+                        if (!ledOn)
+                        {
+                            BMCWEB_LOG_DEBUG
+                                << "Get enclosure identity led failed";
+                            messages::internalError(aResp->res);
+                            return;
+                        }
+
+                        if (*ledOn)
+                        {
+                            aResp->res.jsonValue["LocationIndicatorActive"] =
+                                true;
+                        }
+                        else
+                        {
+                            aResp->res.jsonValue["LocationIndicatorActive"] =
+                                false;
+                        }
+                    }
+                    return;
+                },
+                "xyz.openbmc_project.LED.GroupManager",
+                "/xyz/openbmc_project/led/groups/enclosure_identify",
+                "org.freedesktop.DBus.Properties", "Get",
+                "xyz.openbmc_project.Led.Group", "Asserted");
+        },
+        "xyz.openbmc_project.LED.GroupManager",
+        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
+        "org.freedesktop.DBus.Properties", "Get",
+        "xyz.openbmc_project.Led.Group", "Asserted");
+}
+
+/**
+ * @brief Sets identify led group properties
+ *
+ * @param[in] aResp     Shared pointer for generating response message.
+ * @param[in] ledState  LED state passed from request
+ *
+ * @return None.
+ */
+inline void setLocationIndicatorActive(const std::shared_ptr<AsyncResp>& aResp,
+                                       const bool ledState)
+{
+    BMCWEB_LOG_DEBUG << "Set LocationIndicatorActive";
+
+    crow::connections::systemBus->async_method_call(
+        [aResp, ledState](const boost::system::error_code ec) mutable {
+            if (ec)
+            {
+                // Some systems may not have enclosure_identify_blink object so
+                // lets set enclosure_identify state also if
+                // enclosure_identify_blink failed
+                crow::connections::systemBus->async_method_call(
+                    [aResp](const boost::system::error_code ec2) {
+                        if (ec2)
+                        {
+                            BMCWEB_LOG_DEBUG << "DBUS response error " << ec2;
+                            messages::internalError(aResp->res);
+                            return;
+                        }
+                    },
+                    "xyz.openbmc_project.LED.GroupManager",
+                    "/xyz/openbmc_project/led/groups/enclosure_identify",
+                    "org.freedesktop.DBus.Properties", "Set",
+                    "xyz.openbmc_project.Led.Group", "Asserted",
+                    std::variant<bool>(ledState));
+            }
+        },
+        "xyz.openbmc_project.LED.GroupManager",
+        "/xyz/openbmc_project/led/groups/enclosure_identify_blink",
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Led.Group", "Asserted",
+        std::variant<bool>(ledState));
+}
 } // namespace redfish
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 5f9801b..ef178ed 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1971,7 +1971,7 @@
     void doGet(crow::Response& res, const crow::Request&,
                const std::vector<std::string>&) override
     {
-        res.jsonValue["@odata.type"] = "#ComputerSystem.v1_12_0.ComputerSystem";
+        res.jsonValue["@odata.type"] = "#ComputerSystem.v1_13_0.ComputerSystem";
         res.jsonValue["Name"] = "system";
         res.jsonValue["Id"] = "system";
         res.jsonValue["SystemType"] = "Physical";
@@ -2041,6 +2041,8 @@
                 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
         });
 
+        getLocationIndicatorActive(asyncResp);
+        // TODO (Gunnar): Remove IndicatorLED after enough time has passed
         getIndicatorLedState(asyncResp);
         getComputerSystem(asyncResp, health);
         getHostState(asyncResp);
@@ -2058,6 +2060,7 @@
     void doPatch(crow::Response& res, const crow::Request& req,
                  const std::vector<std::string>&) override
     {
+        std::optional<bool> locationIndicatorActive;
         std::optional<std::string> indicatorLed;
         std::optional<nlohmann::json> bootProps;
         std::optional<nlohmann::json> wdtTimerProps;
@@ -2065,10 +2068,11 @@
         std::optional<std::string> powerRestorePolicy;
         auto asyncResp = std::make_shared<AsyncResp>(res);
 
-        if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed, "Boot",
-                                 bootProps, "WatchdogTimer", wdtTimerProps,
-                                 "PowerRestorePolicy", powerRestorePolicy,
-                                 "AssetTag", assetTag))
+        if (!json_util::readJson(
+                req, res, "IndicatorLED", indicatorLed,
+                "LocationIndicatorActive", locationIndicatorActive, "Boot",
+                bootProps, "WatchdogTimer", wdtTimerProps, "PowerRestorePolicy",
+                powerRestorePolicy, "AssetTag", assetTag))
         {
             return;
         }
@@ -2118,6 +2122,12 @@
             }
         }
 
+        if (locationIndicatorActive)
+        {
+            setLocationIndicatorActive(asyncResp, *locationIndicatorActive);
+        }
+
+        // TODO (Gunnar): Remove IndicatorLED after enough time has passed
         if (indicatorLed)
         {
             setIndicatorLedState(asyncResp, *indicatorLed);