Move source files into application-specific sub-directories

Currently, dbus-sensors implement multiple applications:
 - psusensor
 - adcsensor
 - intelcpusensor
 - hwmontempsensor
 - ipmbsensor
 - nvmesensor
 - externalsensor
 - mcutempsensor
 - intrusionsensor
 - fansensor
 - exitairtempsensor

This commit is to create separate directories for each application so
that things can be separated more easily and the files are smaller,
instead of creating one huge file for the sensor implementation.

There was some discussion in discord on this. [1][2]

[1]: https://discord.com/channels/775381525260664832/1187158775438778408/1284106093756289067
[2]: https://discord.com/channels/775381525260664832/867820390406422538/1303217796821553214

Signed-off-by: George Liu <liuxiwei@ieisystem.com>
Change-Id: I258fc2ee7d8f939c7b83a07350395e78775b2b8d
diff --git a/src/ipmb/IpmbSDRSensor.cpp b/src/ipmb/IpmbSDRSensor.cpp
new file mode 100644
index 0000000..c23ddea
--- /dev/null
+++ b/src/ipmb/IpmbSDRSensor.cpp
@@ -0,0 +1,352 @@
+#include "IpmbSDRSensor.hpp"
+
+#include <sdbusplus/asio/connection.hpp>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+const constexpr char* ipmbService = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
+const constexpr char* ipmbDbusPath = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
+const constexpr char* ipmbInterface = "org.openbmc.Ipmb";
+const constexpr char* ipmbMethod = "sendRequest";
+static constexpr uint8_t lun = 0;
+
+IpmbSDRDevice::IpmbSDRDevice(
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+    uint8_t cmdAddr) :
+    commandAddress(cmdAddr << 2), hostIndex(cmdAddr + 1), conn(dbusConnection)
+{}
+
+bool validateStatus(boost::system::error_code ec,
+                    const IpmbMethodType& response, int hostIndex)
+{
+    if (ec)
+    {
+        return false;
+    }
+
+    const int status = std::get<0>(response);
+    if (status != 0)
+    {
+        std::cerr << "Error reading from IPMB SDR for host " << hostIndex
+                  << "\n";
+        return false;
+    }
+    return true;
+}
+
+/* This function will store the record count of the SDR sensors for each IPMB
+ * bus */
+void IpmbSDRDevice::getSDRRepositoryInfo()
+{
+    std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+    conn->async_method_call(
+        [weakRef](boost::system::error_code ec,
+                  const IpmbMethodType& response) {
+            auto self = weakRef.lock();
+            if (!self)
+            {
+                return;
+            }
+
+            auto status = std::bind_front(validateStatus, ec, response);
+            if (!status(self->hostIndex))
+            {
+                return;
+            }
+
+            const std::vector<uint8_t>& data = std::get<5>(response);
+            const size_t sdrInfoDataSize = 14;
+
+            if (data.size() < sdrInfoDataSize)
+            {
+                std::cerr
+                    << " IPMB Get SDR Repository Info data is empty for host "
+                    << self->hostIndex << "\n";
+                return;
+            }
+
+            constexpr uint8_t recordCountLSB = 1;
+            constexpr uint8_t recordCountMSB = 2;
+
+            uint16_t recordCount = (data[recordCountMSB] << 8) |
+                                   data[recordCountLSB];
+
+            self->reserveSDRRepository(recordCount);
+        },
+        ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+        sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdrInfo, sdrCommandData);
+}
+
+/* This function will store the reserve ID for each IPMB bus index */
+void IpmbSDRDevice::reserveSDRRepository(uint16_t recordCount)
+{
+    std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+    conn->async_method_call(
+        [weakRef, recordCount](boost::system::error_code ec,
+                               const IpmbMethodType& response) {
+            auto self = weakRef.lock();
+            if (!self)
+            {
+                return;
+            }
+
+            auto status = std::bind_front(validateStatus, ec, response);
+            if (!status(self->hostIndex))
+            {
+                return;
+            }
+
+            const std::vector<uint8_t>& data = std::get<5>(response);
+            const size_t sdrReserveDataSize = 2;
+
+            if (data.size() < sdrReserveDataSize)
+            {
+                std::cerr
+                    << " IPMB SDR Reserve Repository data is empty for host "
+                    << self->hostIndex << "\n";
+                return;
+            }
+            uint8_t resrvIDLSB = data[0];
+            uint8_t resrvIDMSB = data[1];
+
+            self->getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+        },
+        ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+        sdr::netfnStorageReq, lun, sdr::cmdStorageReserveSdr, sdrCommandData);
+}
+
+/* This function will read all the information related to the sensor
+ * such as name, threshold value, unit, device address, SDR type */
+void IpmbSDRDevice::getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
+                                     uint8_t resrvIDMSB)
+{
+    std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+    uint8_t loopCount = sdr::perCountByte * iCnt;
+    std::vector<uint8_t> commandData = {resrvIDLSB,      resrvIDMSB,
+                                        nextRecordIDLSB, nextRecordIDMSB,
+                                        loopCount,       sdr::perCountByte};
+
+    conn->async_method_call(
+        [weakRef, recordCount, resrvIDLSB, resrvIDMSB](
+            boost::system::error_code ec, const IpmbMethodType& response) {
+            auto self = weakRef.lock();
+            if (!self)
+            {
+                return;
+            }
+
+            auto status = std::bind_front(validateStatus, ec, response);
+            if (!status(self->hostIndex))
+            {
+                return;
+            }
+
+            const std::vector<uint8_t>& data = std::get<5>(response);
+            const size_t sdrSensorDataSize = 18;
+
+            if (data.size() < sdrSensorDataSize)
+            {
+                std::cerr << "IPMB SDR sensor data is empty for host "
+                          << self->hostIndex << "\n";
+                return;
+            }
+
+            self->handleSDRData(data, recordCount, resrvIDLSB, resrvIDMSB);
+        },
+        ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+        sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdr, commandData);
+}
+
+/* This function will handle the sensor data received by IPMB response */
+void IpmbSDRDevice::handleSDRData(const std::vector<uint8_t>& data,
+                                  uint16_t recordCount, uint8_t resrvIDLSB,
+                                  uint8_t resrvIDMSB)
+{
+    sdrData.insert(sdrData.end(), data.begin(), data.end());
+
+    /* dataLength represents the size of data for SDR types */
+    uint8_t dataLength = sdrData[sdr::dataLengthByte] + sdr::dataLengthByte + 1;
+
+    /*  If sdrData size is less than dataLength, it will call getSDRSensorData
+     *  function recursively till all the data is received.
+     */
+    if (sdrData.size() < dataLength)
+    {
+        iCnt++;
+        getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+    }
+    else
+    {
+        /*  After all the data is received, it is passed to checkSDRData
+         *  function. Next sensor record ID is stored based on the previous
+         *  record ID. Vector of sdrData is cleared to store next sensor data.
+         *  validRecordCount is incremented and getSDRSensorData function is
+         *  called to proceed with next set of sensors.
+         */
+        checkSDRData(sdrData, dataLength);
+        iCnt = 0;
+        nextRecordIDLSB = sdrData[sdr::sdrNxtRecLSB];
+        nextRecordIDMSB = sdrData[sdr::sdrNxtRecMSB];
+        sdrData.clear();
+
+        if (validRecordCount == recordCount)
+        {
+            /* Once all the sensors are read and recordCount matched, it will
+             * return. */
+            nextRecordIDLSB = 0;
+            nextRecordIDMSB = 0;
+            return;
+        }
+        validRecordCount++;
+        getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+    }
+}
+
+/* This function will convert the SDR sensor data such as sensor unit, name, ID,
+ * type from decimal to readable format */
+void IpmbSDRDevice::checkSDRData(std::vector<uint8_t>& sdrDataBytes,
+                                 uint8_t dataLength) const
+{
+    if (sdrDataBytes.size() < dataLength)
+    {
+        return;
+    }
+
+    /* sdrType represents the SDR Type (Byte 5) such as 1, 2, 3 */
+    uint8_t sdrType = sdrDataBytes[sdr::sdrType];
+    if (sdrType != static_cast<uint8_t>(SDRType::sdrType01))
+    {
+        return;
+    }
+
+    /*  dataLen represents the data length (Byte 6) for SDR sensor */
+    int dataLen = sdrDataBytes[sdr::dataLengthByte];
+
+    /* iStrLen represents the length of the sensor name for SDR Type 1 */
+    const uint8_t sdrLenBit = 0x1F;
+    int strLen = (sdrDataBytes[sdrtype01::nameLengthByte]) & (sdrLenBit);
+
+    /* iStrAddr represents the starting byte (Byte 56) for SDR sensor name */
+    int strAddr = dataLen + ((dataLen / (sdr::perCountByte)) * 4) -
+                  (strLen - 1);
+
+    /* Below for loop will convert the bytes to string and form a sensor name */
+
+    std::string tempName(sdrDataBytes.begin() + strAddr,
+                         sdrDataBytes.begin() + strAddr + strLen);
+
+    checkSDRType01Threshold(sdrDataBytes, (hostIndex - 1), tempName);
+}
+
+/* This function will convert the raw value of threshold for each sensor */
+void IpmbSDRDevice::checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
+                                            int busIndex, std::string tempName)
+{
+    const uint8_t bitShiftMsb = 2;
+    const uint8_t sdrThresAccess = 0x0C;
+
+    /* linear represents the sensor's linearization (Byte 27) */
+    uint8_t linear = sdrDataBytes[sdrtype01::sdrLinearByte];
+    if (linear != 0)
+    {
+        return;
+    }
+
+    /* sdrSensCapability (Byte 13) and(&) with sdrThresAccess(0x0C) will declare
+     * whether threshold is present for each sensor */
+    int threshold = (sdrDataBytes[sdrtype01::sensorCapability]) &
+                    (sdrThresAccess);
+
+    /* mData        - 10 bits
+     * mDataByte    - Byte 28 - 8 bits LSB
+     * mTolDataByte - Byte 29 - 2 bits MSB [7-6]
+     */
+    uint16_t mData =
+        ((sdrDataBytes[sdrtype01::mTolDataByte] & 0xC0) << bitShiftMsb) |
+        sdrDataBytes[sdrtype01::mDataByte];
+
+    /* bData        - 10 bits
+     * bDataByte    - Byte 30 - 8 bits LSB
+     * bAcuDataByte - Byte 31 - 2 bits MSB [7-6]
+     */
+    uint16_t bData =
+        ((sdrDataBytes[sdrtype01::bAcuDataByte] & 0xC0) << bitShiftMsb) |
+        sdrDataBytes[sdrtype01::bDataByte];
+
+    /* rbExpDataByte (Byte 33) represents the exponent value
+     *  Bit [3-0] - B Exponent 2's complement signed bit.
+     *  Bit [7-4] - R Exponent 2's complement signed bit.
+     */
+    int8_t bExpVal = sdrDataBytes[sdrtype01::rbExpDataByte] & 0xF;
+    if (bExpVal > 7)
+    {
+        bExpVal = (~bExpVal + 1) & 0xF;
+    }
+
+    /* Shifting the data to right by 4, since rExpVal has 4 bits from 4 to 7 in
+     * byte 33 */
+    int8_t rExpVal = (sdrDataBytes[sdrtype01::rbExpDataByte] >> 4) & 0xF;
+    if (rExpVal > 7)
+    {
+        rExpVal = (~rExpVal + 1) & 0xF;
+        rExpVal = -rExpVal;
+    }
+
+    /* Sensor Threshold Reading Conversion
+     *
+     *  Y = ((Mx + (B * 10^K1)) * (10^K2))
+     *
+     *  X  - Raw value of threshold
+     *  M  - mData Value
+     *  B  - bData Value
+     *  K1 - Signed Exponent of bExpVal
+     *  K2 - Signed Exponent of rExpVal
+     */
+
+    double bDataVal = bData * pow(10, bExpVal);
+    double expVal = pow(10, rExpVal);
+
+    double thresUpCri =
+        sensorValCalculation(mData, bDataVal, expVal,
+                             sdrDataBytes[sdrtype01::upperCriticalThreshold]);
+    double thresLoCri =
+        sensorValCalculation(mData, bDataVal, expVal,
+                             sdrDataBytes[sdrtype01::lowerCriticalThreshold]);
+
+    struct SensorInfo temp;
+
+    temp.sensorReadName = std::move(tempName);
+    temp.sensorUnit = sdrDataBytes[sdrtype01::sdrUnitType];
+
+    temp.thresUpperCri = thresUpCri;
+    temp.thresLowerCri = thresLoCri;
+
+    temp.sensorNumber = sdrDataBytes[sdr::sdrSensorNum];
+    temp.sensCap = threshold;
+
+    sensorRecord[busIndex].emplace_back(std::move(temp));
+
+    SensorValConversion val = {mData, bDataVal, expVal,
+                               sdrDataBytes[sdrtype01::sdrNegHandle]};
+
+    sensorValRecord[busIndex][sdrDataBytes[sdr::sdrSensorNum]] = val;
+}
+
+/* This function will calculate the sensor's threshold value */
+double IpmbSDRDevice::sensorValCalculation(uint16_t mValue, double bValue,
+                                           double expValue, double value)
+{
+    double sensorValue = ((mValue * value) + bValue) * expValue;
+    return sensorValue;
+}
diff --git a/src/ipmb/IpmbSDRSensor.hpp b/src/ipmb/IpmbSDRSensor.hpp
new file mode 100644
index 0000000..8b0a393
--- /dev/null
+++ b/src/ipmb/IpmbSDRSensor.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <sensor.hpp>
+
+using IpmbMethodType =
+    std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+enum class SDRType
+{
+    sdrType01 = 1,
+    sdrType02 = 2,
+    sdrType03 = 3
+};
+
+namespace sdr
+{
+// IPMB Commands
+static constexpr uint8_t netfnStorageReq = 0x0a;
+static constexpr uint8_t cmdStorageGetSdrInfo = 0x20;
+static constexpr uint8_t cmdStorageReserveSdr = 0x22;
+static constexpr uint8_t cmdStorageGetSdr = 0x23;
+
+// Get SDR Commands
+static constexpr uint8_t sdrNxtRecLSB = 0;
+static constexpr uint8_t sdrNxtRecMSB = 1;
+static constexpr uint8_t perCountByte = 16;
+
+// Sensor Record Bytes
+static constexpr uint8_t sdrType = 5;
+static constexpr uint8_t dataLengthByte = 6;
+static constexpr uint8_t sdrSensorNum = 9;
+
+} // namespace sdr
+
+namespace sdrtype01
+{
+// Negative Handle Commands
+static constexpr uint8_t maxPosReadingMargin = 127;
+static constexpr uint8_t twosCompVal = 128;
+static constexpr double thermalConst = 256;
+
+static constexpr uint8_t sdrSensNoThres = 0;
+static constexpr uint8_t sensorCapability = 13;
+static constexpr uint8_t sdrNegHandle = 24;
+static constexpr uint8_t sdrUnitType = 25;
+static constexpr uint8_t sdrLinearByte = 27;
+
+// SDR Type 1 Thresholds Commands
+static constexpr uint8_t mDataByte = 28;
+static constexpr uint8_t mTolDataByte = 29;
+static constexpr uint8_t bDataByte = 30;
+static constexpr uint8_t bAcuDataByte = 31;
+static constexpr uint8_t rbExpDataByte = 33;
+static constexpr uint8_t upperCriticalThreshold = 43;
+static constexpr uint8_t lowerCriticalThreshold = 46;
+static constexpr uint8_t nameLengthByte = 53;
+
+} // namespace sdrtype01
+
+struct SensorInfo
+{
+    std::string sensorReadName;
+    uint8_t sensorUnit = 0;
+    double thresUpperCri = 0;
+    double thresLowerCri = 0;
+    uint8_t sensorNumber = 0;
+    uint8_t sensCap = 0;
+};
+
+struct SensorValConversion
+{
+    uint16_t mValue = 0;
+    double bValue = 0;
+    double expoVal = 0;
+    uint8_t negRead = 0;
+};
+
+inline std::map<int, std::vector<SensorInfo>> sensorRecord;
+inline std::map<int, std::map<uint8_t, SensorValConversion>> sensorValRecord;
+
+class IpmbSDRDevice : public std::enable_shared_from_this<IpmbSDRDevice>
+{
+  public:
+    IpmbSDRDevice(std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+                  uint8_t cmdAddr);
+
+    uint8_t commandAddress = 0;
+    int hostIndex = 0;
+
+    std::shared_ptr<sdbusplus::asio::connection> conn;
+
+    std::vector<uint8_t> sdrData;
+    uint16_t validRecordCount = 1;
+    uint8_t iCnt = 0;
+    uint8_t nextRecordIDLSB = 0;
+    uint8_t nextRecordIDMSB = 0;
+
+    std::vector<uint8_t> sdrCommandData;
+
+    void getSDRRepositoryInfo();
+
+    void reserveSDRRepository(uint16_t recordCount);
+
+    void getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
+                          uint8_t resrvIDMSB);
+
+    void handleSDRData(const std::vector<uint8_t>& data, uint16_t recordCount,
+                       uint8_t resrvIDLSB, uint8_t resrvIDMSB);
+
+    void checkSDRData(std::vector<uint8_t>& sdrDataBytes,
+                      uint8_t dataLength) const;
+
+    static void checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
+                                        int busIndex, std::string tempName);
+
+    inline static double sensorValCalculation(uint16_t mValue, double bValue,
+                                              double expValue, double value);
+};
diff --git a/src/ipmb/IpmbSensor.cpp b/src/ipmb/IpmbSensor.cpp
new file mode 100644
index 0000000..fc6cc7a
--- /dev/null
+++ b/src/ipmb/IpmbSensor.cpp
@@ -0,0 +1,714 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "IpmbSensor.hpp"
+
+#include "IpmbSDRSensor.hpp"
+#include "SensorPaths.hpp"
+#include "Thresholds.hpp"
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/error.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/message/native_types.hpp>
+
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <variant>
+#include <vector>
+
+constexpr const bool debug = false;
+
+static constexpr double ipmbMaxReading = 0xFF;
+static constexpr double ipmbMinReading = 0;
+
+static constexpr uint8_t meAddress = 1;
+static constexpr uint8_t lun = 0;
+static constexpr uint8_t hostSMbusIndexDefault = 0x03;
+static constexpr uint8_t ipmbBusIndexDefault = 0;
+static constexpr float pollRateDefault = 1; // in seconds
+
+static constexpr const char* sensorPathPrefix = "/xyz/openbmc_project/sensors/";
+
+IpmbSensor::IpmbSensor(
+    std::shared_ptr<sdbusplus::asio::connection>& conn,
+    boost::asio::io_context& io, const std::string& sensorName,
+    const std::string& sensorConfiguration,
+    sdbusplus::asio::object_server& objectServer,
+    std::vector<thresholds::Threshold>&& thresholdData, uint8_t deviceAddress,
+    uint8_t hostSMbusIndex, const float pollRate, std::string& sensorTypeName) :
+    Sensor(escapeName(sensorName), std::move(thresholdData),
+           sensorConfiguration, "IpmbSensor", false, false, ipmbMaxReading,
+           ipmbMinReading, conn, PowerState::on),
+    deviceAddress(deviceAddress), hostSMbusIndex(hostSMbusIndex),
+    sensorPollMs(static_cast<int>(pollRate * 1000)), objectServer(objectServer),
+    waitTimer(io)
+{
+    std::string dbusPath = sensorPathPrefix + sensorTypeName + "/" + name;
+
+    sensorInterface = objectServer.add_interface(
+        dbusPath, "xyz.openbmc_project.Sensor.Value");
+
+    for (const auto& threshold : thresholds)
+    {
+        std::string interface = thresholds::getInterface(threshold.level);
+        thresholdInterfaces[static_cast<size_t>(threshold.level)] =
+            objectServer.add_interface(dbusPath, interface);
+    }
+    association = objectServer.add_interface(dbusPath, association::interface);
+}
+
+IpmbSensor::~IpmbSensor()
+{
+    waitTimer.cancel();
+    for (const auto& iface : thresholdInterfaces)
+    {
+        objectServer.remove_interface(iface);
+    }
+    objectServer.remove_interface(sensorInterface);
+    objectServer.remove_interface(association);
+}
+
+std::string IpmbSensor::getSubTypeUnits() const
+{
+    switch (subType)
+    {
+        case IpmbSubType::temp:
+            return sensor_paths::unitDegreesC;
+        case IpmbSubType::curr:
+            return sensor_paths::unitAmperes;
+        case IpmbSubType::power:
+            return sensor_paths::unitWatts;
+        case IpmbSubType::volt:
+            return sensor_paths::unitVolts;
+        case IpmbSubType::util:
+            return sensor_paths::unitPercent;
+        default:
+            throw std::runtime_error("Invalid sensor type");
+    }
+}
+
+void IpmbSensor::init()
+{
+    loadDefaults();
+    setInitialProperties(getSubTypeUnits());
+    if (initCommand)
+    {
+        runInitCmd();
+    }
+    read();
+}
+
+static void initCmdCb(const std::weak_ptr<IpmbSensor>& weakRef,
+                      const boost::system::error_code& ec,
+                      const IpmbMethodType& response)
+{
+    std::shared_ptr<IpmbSensor> self = weakRef.lock();
+    if (!self)
+    {
+        return;
+    }
+    const int& status = std::get<0>(response);
+    if (ec || (status != 0))
+    {
+        std::cerr << "Error setting init command for device: " << self->name
+                  << "\n";
+    }
+}
+
+void IpmbSensor::runInitCmd()
+{
+    if (!initCommand)
+    {
+        return;
+    }
+    dbusConnection->async_method_call(
+        [weakRef{weak_from_this()}](const boost::system::error_code& ec,
+                                    const IpmbMethodType& response) {
+            initCmdCb(weakRef, ec, response);
+        },
+        "xyz.openbmc_project.Ipmi.Channel.Ipmb",
+        "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
+        "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
+}
+
+void IpmbSensor::loadDefaults()
+{
+    if (type == IpmbType::meSensor)
+    {
+        commandAddress = meAddress;
+        netfn = ipmi::sensor::netFn;
+        command = ipmi::sensor::getSensorReading;
+        commandData = {deviceAddress};
+        readingFormat = ReadingFormat::byte0;
+    }
+    else if (type == IpmbType::PXE1410CVR)
+    {
+        commandAddress = meAddress;
+        netfn = ipmi::me_bridge::netFn;
+        command = ipmi::me_bridge::sendRawPmbus;
+        initCommand = ipmi::me_bridge::sendRawPmbus;
+        // pmbus read temp
+        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                       deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                       0x01,          0x02, 0x8d};
+        // goto page 0
+        initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
+                    deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                    0x02,          0x00, 0x00, 0x00};
+        readingFormat = ReadingFormat::linearElevenBit;
+    }
+    else if (type == IpmbType::IR38363VR)
+    {
+        commandAddress = meAddress;
+        netfn = ipmi::me_bridge::netFn;
+        command = ipmi::me_bridge::sendRawPmbus;
+        // pmbus read temp
+        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                       deviceAddress, 00,   0x00, 0x00, 0x00,
+                       0x01,          0x02, 0x8D};
+        readingFormat = ReadingFormat::elevenBitShift;
+    }
+    else if (type == IpmbType::ADM1278HSC)
+    {
+        commandAddress = meAddress;
+        uint8_t snsNum = 0;
+        switch (subType)
+        {
+            case IpmbSubType::temp:
+            case IpmbSubType::curr:
+                if (subType == IpmbSubType::temp)
+                {
+                    snsNum = 0x8d;
+                }
+                else
+                {
+                    snsNum = 0x8c;
+                }
+                netfn = ipmi::me_bridge::netFn;
+                command = ipmi::me_bridge::sendRawPmbus;
+                commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
+                               0x00, 0x00, 0x01, 0x02, snsNum};
+                readingFormat = ReadingFormat::elevenBit;
+                break;
+            case IpmbSubType::power:
+            case IpmbSubType::volt:
+                netfn = ipmi::sensor::netFn;
+                command = ipmi::sensor::getSensorReading;
+                commandData = {deviceAddress};
+                readingFormat = ReadingFormat::byte0;
+                break;
+            default:
+                throw std::runtime_error("Invalid sensor type");
+        }
+    }
+    else if (type == IpmbType::mpsVR)
+    {
+        commandAddress = meAddress;
+        netfn = ipmi::me_bridge::netFn;
+        command = ipmi::me_bridge::sendRawPmbus;
+        initCommand = ipmi::me_bridge::sendRawPmbus;
+        // pmbus read temp
+        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                       deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                       0x01,          0x02, 0x8d};
+        // goto page 0
+        initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
+                    deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                    0x02,          0x00, 0x00, 0x00};
+        readingFormat = ReadingFormat::byte3;
+    }
+    else if (type == IpmbType::SMPro)
+    {
+        // This is an Ampere SMPro reachable via a BMC.  For example,
+        // this architecture is used on ADLINK Ampere Altra systems.
+        // See the Ampere Family SoC BMC Interface Specification at
+        // https://amperecomputing.com/customer-connect/products/altra-family-software---firmware
+        // for details of the sensors.
+        commandAddress = 0;
+        netfn = 0x30;
+        command = 0x31;
+        commandData = {0x9e, deviceAddress};
+        switch (subType)
+        {
+            case IpmbSubType::temp:
+                readingFormat = ReadingFormat::nineBit;
+                break;
+            case IpmbSubType::power:
+                readingFormat = ReadingFormat::tenBit;
+                break;
+            case IpmbSubType::curr:
+            case IpmbSubType::volt:
+                readingFormat = ReadingFormat::fifteenBit;
+                break;
+            default:
+                throw std::runtime_error("Invalid sensor type");
+        }
+    }
+    else
+    {
+        throw std::runtime_error("Invalid sensor type");
+    }
+
+    if (subType == IpmbSubType::util)
+    {
+        // Utilization need to be scaled to percent
+        maxValue = 100;
+        minValue = 0;
+    }
+}
+
+void IpmbSensor::checkThresholds()
+{
+    thresholds::checkThresholds(this);
+}
+
+bool IpmbSensor::processReading(ReadingFormat readingFormat, uint8_t command,
+                                const std::vector<uint8_t>& data, double& resp,
+                                size_t errCount)
+{
+    switch (readingFormat)
+    {
+        case (ReadingFormat::byte0):
+        {
+            if (command == ipmi::sensor::getSensorReading &&
+                !ipmi::sensor::isValid(data))
+            {
+                return false;
+            }
+            resp = data[0];
+            return true;
+        }
+        case (ReadingFormat::byte3):
+        {
+            if (data.size() < 4)
+            {
+                if (errCount == 0U)
+                {
+                    std::cerr << "Invalid data length returned\n";
+                }
+                return false;
+            }
+            resp = data[3];
+            return true;
+        }
+        case (ReadingFormat::nineBit):
+        case (ReadingFormat::tenBit):
+        case (ReadingFormat::fifteenBit):
+        {
+            if (data.size() != 2)
+            {
+                if (errCount == 0U)
+                {
+                    std::cerr << "Invalid data length returned\n";
+                }
+                return false;
+            }
+
+            // From the Altra Family SoC BMC Interface Specification:
+            // 0xFFFF – This sensor data is either missing or is not supported
+            // by the device.
+            if ((data[0] == 0xff) && (data[1] == 0xff))
+            {
+                return false;
+            }
+
+            if (readingFormat == ReadingFormat::nineBit)
+            {
+                int16_t value = data[0];
+                if ((data[1] & 0x1) != 0)
+                {
+                    // Sign extend to 16 bits
+                    value |= 0xFF00;
+                }
+                resp = value;
+            }
+            else if (readingFormat == ReadingFormat::tenBit)
+            {
+                uint16_t value = ((data[1] & 0x3) << 8) + data[0];
+                resp = value;
+            }
+            else if (readingFormat == ReadingFormat::fifteenBit)
+            {
+                uint16_t value = ((data[1] & 0x7F) << 8) + data[0];
+                // Convert mV to V
+                resp = value / 1000.0;
+            }
+
+            return true;
+        }
+        case (ReadingFormat::elevenBit):
+        {
+            if (data.size() < 5)
+            {
+                if (errCount == 0U)
+                {
+                    std::cerr << "Invalid data length returned\n";
+                }
+                return false;
+            }
+
+            int16_t value = ((data[4] << 8) | data[3]);
+            resp = value;
+            return true;
+        }
+        case (ReadingFormat::elevenBitShift):
+        {
+            if (data.size() < 5)
+            {
+                if (errCount == 0U)
+                {
+                    std::cerr << "Invalid data length returned\n";
+                }
+                return false;
+            }
+
+            resp = ((data[4] << 8) | data[3]) >> 3;
+            return true;
+        }
+        case (ReadingFormat::linearElevenBit):
+        {
+            if (data.size() < 5)
+            {
+                if (errCount == 0U)
+                {
+                    std::cerr << "Invalid data length returned\n";
+                }
+                return false;
+            }
+
+            int16_t value = ((data[4] << 8) | data[3]);
+            constexpr const size_t shift = 16 - 11; // 11bit into 16bit
+            value <<= shift;
+            value >>= shift;
+            resp = value;
+            return true;
+        }
+        default:
+            throw std::runtime_error("Invalid reading type");
+    }
+}
+
+void IpmbSensor::ipmbRequestCompletionCb(const boost::system::error_code& ec,
+                                         const IpmbMethodType& response)
+{
+    const int& status = std::get<0>(response);
+    if (ec || (status != 0))
+    {
+        incrementError();
+        read();
+        return;
+    }
+    const std::vector<uint8_t>& data = std::get<5>(response);
+    if constexpr (debug)
+    {
+        std::cout << name << ": ";
+        for (size_t d : data)
+        {
+            std::cout << d << " ";
+        }
+        std::cout << "\n";
+    }
+    if (data.empty())
+    {
+        incrementError();
+        read();
+        return;
+    }
+
+    double value = 0;
+
+    if (!processReading(readingFormat, command, data, value, errCount))
+    {
+        incrementError();
+        read();
+        return;
+    }
+
+    // rawValue only used in debug logging
+    // up to 5th byte in data are used to derive value
+    size_t end = std::min(sizeof(uint64_t), data.size());
+    uint64_t rawData = 0;
+    for (size_t i = 0; i < end; i++)
+    {
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+        reinterpret_cast<uint8_t*>(&rawData)[i] = data[i];
+    }
+    rawValue = static_cast<double>(rawData);
+
+    /* Adjust value as per scale and offset */
+    value = (value * scaleVal) + offsetVal;
+    updateValue(value);
+    read();
+}
+
+void IpmbSensor::read()
+{
+    waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
+    waitTimer.async_wait(
+        [weakRef{weak_from_this()}](const boost::system::error_code& ec) {
+            if (ec == boost::asio::error::operation_aborted)
+            {
+                return; // we're being canceled
+            }
+            std::shared_ptr<IpmbSensor> self = weakRef.lock();
+            if (!self)
+            {
+                return;
+            }
+            self->sendIpmbRequest();
+        });
+}
+
+void IpmbSensor::sendIpmbRequest()
+{
+    if (!readingStateGood())
+    {
+        updateValue(std::numeric_limits<double>::quiet_NaN());
+        read();
+        return;
+    }
+    dbusConnection->async_method_call(
+        [weakRef{weak_from_this()}](boost::system::error_code ec,
+                                    const IpmbMethodType& response) {
+            std::shared_ptr<IpmbSensor> self = weakRef.lock();
+            if (!self)
+            {
+                return;
+            }
+            self->ipmbRequestCompletionCb(ec, response);
+        },
+        "xyz.openbmc_project.Ipmi.Channel.Ipmb",
+        "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
+        "sendRequest", commandAddress, netfn, lun, command, commandData);
+}
+
+bool IpmbSensor::sensorClassType(const std::string& sensorClass)
+{
+    if (sensorClass == "PxeBridgeTemp")
+    {
+        type = IpmbType::PXE1410CVR;
+    }
+    else if (sensorClass == "IRBridgeTemp")
+    {
+        type = IpmbType::IR38363VR;
+    }
+    else if (sensorClass == "HSCBridge")
+    {
+        type = IpmbType::ADM1278HSC;
+    }
+    else if (sensorClass == "MpsBridgeTemp")
+    {
+        type = IpmbType::mpsVR;
+    }
+    else if (sensorClass == "METemp" || sensorClass == "MESensor")
+    {
+        type = IpmbType::meSensor;
+    }
+    else if (sensorClass == "SMPro")
+    {
+        type = IpmbType::SMPro;
+    }
+    else
+    {
+        std::cerr << "Invalid class " << sensorClass << "\n";
+        return false;
+    }
+    return true;
+}
+
+void IpmbSensor::sensorSubType(const std::string& sensorTypeName)
+{
+    if (sensorTypeName == "voltage")
+    {
+        subType = IpmbSubType::volt;
+    }
+    else if (sensorTypeName == "power")
+    {
+        subType = IpmbSubType::power;
+    }
+    else if (sensorTypeName == "current")
+    {
+        subType = IpmbSubType::curr;
+    }
+    else if (sensorTypeName == "utilization")
+    {
+        subType = IpmbSubType::util;
+    }
+    else
+    {
+        subType = IpmbSubType::temp;
+    }
+}
+
+void IpmbSensor::parseConfigValues(const SensorBaseConfigMap& entry)
+{
+    auto findScaleVal = entry.find("ScaleValue");
+    if (findScaleVal != entry.end())
+    {
+        scaleVal = std::visit(VariantToDoubleVisitor(), findScaleVal->second);
+    }
+
+    auto findOffsetVal = entry.find("OffsetValue");
+    if (findOffsetVal != entry.end())
+    {
+        offsetVal = std::visit(VariantToDoubleVisitor(), findOffsetVal->second);
+    }
+
+    readState = getPowerState(entry);
+}
+
+void createSensors(
+    boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
+    boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
+        sensors,
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+    if (!dbusConnection)
+    {
+        std::cerr << "Connection not created\n";
+        return;
+    }
+    dbusConnection->async_method_call(
+        [&](boost::system::error_code ec, const ManagedObjectType& resp) {
+            if (ec)
+            {
+                std::cerr << "Error contacting entity manager\n";
+                return;
+            }
+            for (const auto& [path, interfaces] : resp)
+            {
+                for (const auto& [intf, cfg] : interfaces)
+                {
+                    if (intf != configInterfaceName(sensorType))
+                    {
+                        continue;
+                    }
+                    std::string name = loadVariant<std::string>(cfg, "Name");
+
+                    std::vector<thresholds::Threshold> sensorThresholds;
+                    if (!parseThresholdsFromConfig(interfaces,
+                                                   sensorThresholds))
+                    {
+                        std::cerr
+                            << "error populating thresholds " << name << "\n";
+                    }
+                    uint8_t deviceAddress =
+                        loadVariant<uint8_t>(cfg, "Address");
+
+                    std::string sensorClass =
+                        loadVariant<std::string>(cfg, "Class");
+
+                    uint8_t hostSMbusIndex = hostSMbusIndexDefault;
+                    auto findSmType = cfg.find("HostSMbusIndex");
+                    if (findSmType != cfg.end())
+                    {
+                        hostSMbusIndex = std::visit(
+                            VariantToUnsignedIntVisitor(), findSmType->second);
+                    }
+
+                    float pollRate = getPollRate(cfg, pollRateDefault);
+
+                    uint8_t ipmbBusIndex = ipmbBusIndexDefault;
+                    auto findBusType = cfg.find("Bus");
+                    if (findBusType != cfg.end())
+                    {
+                        ipmbBusIndex = std::visit(VariantToUnsignedIntVisitor(),
+                                                  findBusType->second);
+                        std::cerr << "Ipmb Bus Index for " << name << " is "
+                                  << static_cast<int>(ipmbBusIndex) << "\n";
+                    }
+
+                    /* Default sensor type is "temperature" */
+                    std::string sensorTypeName = "temperature";
+                    auto findType = cfg.find("SensorType");
+                    if (findType != cfg.end())
+                    {
+                        sensorTypeName = std::visit(VariantToStringVisitor(),
+                                                    findType->second);
+                    }
+
+                    auto& sensor = sensors[name];
+                    sensor = nullptr;
+                    sensor = std::make_shared<IpmbSensor>(
+                        dbusConnection, io, name, path, objectServer,
+                        std::move(sensorThresholds), deviceAddress,
+                        hostSMbusIndex, pollRate, sensorTypeName);
+
+                    sensor->parseConfigValues(cfg);
+                    if (!(sensor->sensorClassType(sensorClass)))
+                    {
+                        continue;
+                    }
+                    sensor->sensorSubType(sensorTypeName);
+                    sensor->init();
+                }
+            }
+        },
+        entityManagerName, "/xyz/openbmc_project/inventory",
+        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+}
+
+void interfaceRemoved(
+    sdbusplus::message_t& message,
+    boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
+        sensors)
+{
+    if (message.is_method_error())
+    {
+        std::cerr << "interfacesRemoved callback method error\n";
+        return;
+    }
+
+    sdbusplus::message::object_path removedPath;
+    std::vector<std::string> interfaces;
+
+    message.read(removedPath, interfaces);
+
+    // If the xyz.openbmc_project.Confguration.X interface was removed
+    // for one or more sensors, delete those sensor objects.
+    auto sensorIt = sensors.begin();
+    while (sensorIt != sensors.end())
+    {
+        if ((sensorIt->second->configurationPath == removedPath) &&
+            (std::find(interfaces.begin(), interfaces.end(),
+                       configInterfaceName(sdrInterface)) != interfaces.end()))
+        {
+            sensorIt = sensors.erase(sensorIt);
+        }
+        else
+        {
+            sensorIt++;
+        }
+    }
+}
diff --git a/src/ipmb/IpmbSensor.hpp b/src/ipmb/IpmbSensor.hpp
new file mode 100644
index 0000000..953e44a
--- /dev/null
+++ b/src/ipmb/IpmbSensor.hpp
@@ -0,0 +1,148 @@
+#pragma once
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sensor.hpp>
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+constexpr const char* sensorType = "IpmbSensor";
+constexpr const char* sdrInterface = "IpmbDevice";
+
+enum class IpmbType
+{
+    none,
+    meSensor,
+    PXE1410CVR,
+    IR38363VR,
+    ADM1278HSC,
+    mpsVR,
+    SMPro
+};
+
+enum class IpmbSubType
+{
+    none,
+    temp,
+    curr,
+    power,
+    volt,
+    util
+};
+
+enum class ReadingFormat
+{
+    byte0,
+    byte3,
+    nineBit,
+    tenBit,
+    elevenBit,
+    elevenBitShift,
+    linearElevenBit,
+    fifteenBit
+};
+
+namespace ipmi
+{
+namespace sensor
+{
+constexpr uint8_t netFn = 0x04;
+constexpr uint8_t getSensorReading = 0x2d;
+
+static inline bool isValid(const std::vector<uint8_t>& data)
+{
+    constexpr auto readingUnavailableBit = 5;
+
+    // Proper 'Get Sensor Reading' response has at least 4 bytes, including
+    // Completion Code. Our IPMB stack strips Completion Code from payload so we
+    // compare here against the rest of payload
+    if (data.size() < 3)
+    {
+        return false;
+    }
+
+    // Per IPMI 'Get Sensor Reading' specification
+    if ((data[1] & (1 << readingUnavailableBit)) != 0)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+} // namespace sensor
+namespace me_bridge
+{
+constexpr uint8_t netFn = 0x2e;
+constexpr uint8_t sendRawPmbus = 0xd9;
+} // namespace me_bridge
+} // namespace ipmi
+
+using IpmbMethodType =
+    std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+struct IpmbSensor :
+    public Sensor,
+    public std::enable_shared_from_this<IpmbSensor>
+{
+    IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+               boost::asio::io_context& io, const std::string& name,
+               const std::string& sensorConfiguration,
+               sdbusplus::asio::object_server& objectServer,
+               std::vector<thresholds::Threshold>&& thresholdData,
+               uint8_t deviceAddress, uint8_t hostSMbusIndex, float pollRate,
+               std::string& sensorTypeName);
+    ~IpmbSensor() override;
+
+    void checkThresholds() override;
+    void read();
+    void init();
+    std::string getSubTypeUnits() const;
+    void loadDefaults();
+    void runInitCmd();
+    static bool processReading(ReadingFormat readingFormat, uint8_t command,
+                               const std::vector<uint8_t>& data, double& resp,
+                               size_t errCount);
+    void parseConfigValues(const SensorBaseConfigMap& entry);
+    bool sensorClassType(const std::string& sensorClass);
+    void sensorSubType(const std::string& sensorTypeName);
+
+    IpmbType type = IpmbType::none;
+    IpmbSubType subType = IpmbSubType::none;
+    double scaleVal = 1.0;
+    double offsetVal = 0.0;
+    uint8_t commandAddress = 0;
+    uint8_t netfn = 0;
+    uint8_t command = 0;
+    uint8_t deviceAddress = 0;
+    uint8_t errorCount = 0;
+    uint8_t hostSMbusIndex = 0;
+    std::vector<uint8_t> commandData;
+    std::optional<uint8_t> initCommand;
+    std::vector<uint8_t> initData;
+    int sensorPollMs;
+
+    ReadingFormat readingFormat = ReadingFormat::byte0;
+
+  private:
+    void sendIpmbRequest();
+    sdbusplus::asio::object_server& objectServer;
+    boost::asio::steady_timer waitTimer;
+    void ipmbRequestCompletionCb(const boost::system::error_code& ec,
+                                 const IpmbMethodType& response);
+};
+
+void createSensors(
+    boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
+    boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
+        sensors,
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection);
+
+void interfaceRemoved(
+    sdbusplus::message_t& message,
+    boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
+        sensors);
diff --git a/src/ipmb/IpmbSensorMain.cpp b/src/ipmb/IpmbSensorMain.cpp
new file mode 100644
index 0000000..5e38dd2
--- /dev/null
+++ b/src/ipmb/IpmbSensorMain.cpp
@@ -0,0 +1,161 @@
+#include "IpmbSDRSensor.hpp"
+#include "IpmbSensor.hpp"
+#include "Utils.hpp"
+
+#include <boost/asio/error.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <variant>
+#include <vector>
+
+std::unique_ptr<boost::asio::steady_timer> initCmdTimer;
+boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>> sensors;
+boost::container::flat_map<uint8_t, std::shared_ptr<IpmbSDRDevice>> sdrsensor;
+
+void sdrHandler(
+    boost::container::flat_map<uint8_t, std::shared_ptr<IpmbSDRDevice>>
+        sdrsensor,
+    sdbusplus::message_t& message,
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+    std::string objectName;
+    SensorBaseConfigMap values;
+    message.read(objectName, values);
+
+    auto findBus = values.find("Bus");
+    if (findBus == values.end())
+    {
+        return;
+    }
+
+    uint8_t busIndex = loadVariant<uint8_t>(values, "Bus");
+
+    auto& sdrsen = sdrsensor[busIndex];
+    sdrsen = nullptr;
+    sdrsen = std::make_shared<IpmbSDRDevice>(dbusConnection, busIndex);
+    sdrsen->getSDRRepositoryInfo();
+}
+
+void reinitSensors(sdbusplus::message_t& message)
+{
+    constexpr const size_t reinitWaitSeconds = 2;
+    std::string objectName;
+    boost::container::flat_map<std::string, std::variant<std::string>> values;
+    message.read(objectName, values);
+
+    auto findStatus = values.find(power::property);
+    if (findStatus != values.end())
+    {
+        bool powerStatus =
+            std::get<std::string>(findStatus->second).ends_with(".Running");
+        if (powerStatus)
+        {
+            if (!initCmdTimer)
+            {
+                // this should be impossible
+                return;
+            }
+            // we seem to send this command too fast sometimes, wait before
+            // sending
+            initCmdTimer->expires_after(
+                std::chrono::seconds(reinitWaitSeconds));
+
+            initCmdTimer->async_wait([](const boost::system::error_code ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    return; // we're being canceled
+                }
+
+                for (const auto& [name, sensor] : sensors)
+                {
+                    if (sensor)
+                    {
+                        sensor->runInitCmd();
+                    }
+                }
+            });
+        }
+    }
+}
+
+int main()
+{
+    boost::asio::io_context io;
+    auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+    sdbusplus::asio::object_server objectServer(systemBus, true);
+    objectServer.add_manager("/xyz/openbmc_project/sensors");
+    systemBus->request_name("xyz.openbmc_project.IpmbSensor");
+
+    initCmdTimer = std::make_unique<boost::asio::steady_timer>(io);
+
+    boost::asio::post(io, [&]() {
+        createSensors(io, objectServer, sensors, systemBus);
+    });
+
+    boost::asio::steady_timer configTimer(io);
+
+    std::function<void(sdbusplus::message_t&)> eventHandler =
+        [&](sdbusplus::message_t&) {
+            configTimer.expires_after(std::chrono::seconds(1));
+            // create a timer because normally multiple properties change
+            configTimer.async_wait([&](const boost::system::error_code& ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    return; // we're being canceled
+                }
+                createSensors(io, objectServer, sensors, systemBus);
+                if (sensors.empty())
+                {
+                    std::cout << "Configuration not detected\n";
+                }
+            });
+        };
+
+    std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
+        setupPropertiesChangedMatches(
+            *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
+
+    sdbusplus::bus::match_t powerChangeMatch(
+        static_cast<sdbusplus::bus_t&>(*systemBus),
+        "type='signal',interface='" + std::string(properties::interface) +
+            "',path='" + std::string(power::path) + "',arg0='" +
+            std::string(power::interface) + "'",
+        reinitSensors);
+
+    auto matchSignal = std::make_shared<sdbusplus::bus::match_t>(
+        static_cast<sdbusplus::bus_t&>(*systemBus),
+        "type='signal',member='PropertiesChanged',path_namespace='" +
+            std::string(inventoryPath) + "',arg0namespace='" +
+            configInterfaceName(sdrInterface) + "'",
+        [&systemBus](sdbusplus::message_t& msg) {
+            sdrHandler(sdrsensor, msg, systemBus);
+        });
+
+    // Watch for entity-manager to remove configuration interfaces
+    // so the corresponding sensors can be removed.
+    auto ifaceRemovedMatch = std::make_shared<sdbusplus::bus::match_t>(
+        static_cast<sdbusplus::bus_t&>(*systemBus),
+        "type='signal',member='InterfacesRemoved',arg0path='" +
+            std::string(inventoryPath) + "/'",
+        [](sdbusplus::message_t& msg) { interfaceRemoved(msg, sensors); });
+
+    setupManufacturingModeMatch(*systemBus);
+    io.run();
+    return 0;
+}
diff --git a/src/ipmb/meson.build b/src/ipmb/meson.build
new file mode 100644
index 0000000..d724b80
--- /dev/null
+++ b/src/ipmb/meson.build
@@ -0,0 +1,15 @@
+src_inc = include_directories('..')
+
+executable(
+    'ipmbsensor',
+    'IpmbSensorMain.cpp',
+    'IpmbSensor.cpp',
+    'IpmbSDRSensor.cpp',
+    dependencies: [
+        default_deps,
+        thresholds_dep,
+        utils_dep,
+    ],
+    include_directories: src_inc,
+    install: true,
+)
\ No newline at end of file