Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Detect power change event / UPS state



  • Hi! I want to detect when the system is on UPS or just power state changes.

    Something like this:

    enter image description here

    I tried to detect it using GetSystemPowerStatus function:

    Code:

        SYSTEM_POWER_STATUS powerStatus;
        bool powerStatusRes = GetSystemPowerStatus(&powerStatus);
    
        switch (powerStatus.ACLineStatus) {
            case 0:
                std::cout << "Offline" << std::endl;
                break;
            case 1:
                std::cout << "Online" << std::endl;
                break;
            case 255:
                std::cout << "Unknown status" << std::endl;
            default:
                std::cout << "Failed to detect the status" << std::endl;
        }
    
        switch (powerStatus.BatteryFlag) {
            case 1:
                std::cout << "High — the battery capacity is at more than 66 percent" << std::endl;
                break;
            case 2:
                std::cout << "Low — the battery capacity is at less than 33 percent" << std::endl;
                break;
            case 4:
                std::cout << "Critical — the battery capacity is at less than five percent" << std::endl;
                break;
            case 8:
                std::cout << "Charging" << std::endl;
                break;
            case 128:
                std::cout << "No system battery" << std::endl;
                break;
            case 255:
                std::cout << "Unknown status—unable to read the battery flag information" << std::endl;
                break;
            default:
                std::cout << "Failed to detect the battery flag" << std::endl;
        }
    

    But it returns powerStatus.ACLineStatus for UPS or AC as Online and for powerStatus.BatteryFlag - No system battery.

    Also I have tried to detect the UPS using GetPwrCapabilities function:

    Code:

    SYSTEM_POWER_CAPABILITIES SysPowerCapabilities = {0};
    
    if (!GetPwrCapabilities(&SysPowerCapabilities)){
        std::cout << "Failed to get System Power information!" << std::endl;
    }
    
    if (SysPowerCapabilities.UpsPresent) {
        std::cout << "UPS found" << std::endl;
    } else {
        std::cout << "UPS not found" << std::endl;
    }
    

    It returns - UPS not found (driver is not available for this UPS model).

    Now I'm trying to detect power changes by using the Qt nativeEvent function:

    bool Test::nativeEvent(const QByteArray &eventType, void *message, long *result)
    {
        Q_UNUSED(result);
        Q_UNUSED(eventType);
        MSG *msg = static_cast<MSG*>(message);
    
        if (msg->message == WM_POWERBROADCAST && msg->wParam == PBT_APMPOWERSTATUSCHANGE) {
            qDebug() << "Power changed!!!";
        }
    
        return false;
    }
    

    But nothing is printed to the console. Any ideas how to detect it? Thanks.


  • Lifetime Qt Champion

    Hi,

    If I understand correctly, you don't have the driver for your UPS. How can you expect Qt to catch any related events if your system can't handle the hardware ?



  • @SGaist

    Yes, I don't have the driver for this UPS. So how other apps, for example ViewPower communicate with this UPS? Thanks.


  • Lifetime Qt Champion

    @Cobra91151

    The best would be to ask the authors of ViewPower for that ;)


  • Lifetime Qt Champion

    There are likely generic APIs that the device has to answer to. Like you already did. They may be doing some polling.



  • @SGaist

    By enumerating all usb devices I got the Vendor and Product ID for the UPS. How about talking to the serial port, for example using QSerialPort class?



  • @aha_1980

    Great idea, but this project is deprecated.


  • Lifetime Qt Champion

    @Cobra91151 so now you need to find the documentation for the UPS. It should explain how to control it.



  • @aha_1980

    No tech. documentation is available for this UPS. But I got some values from the net:

     0x00840010 = UPS
     0x00840012 = Battery
     0x00840030 = Voltage
     0x00840040 = ConfigVoltage
     0x0084001a = Input
     0x0084005a = AudibleAlarmControl
     0x00840002 = PresentStatus
     0x00850044 = Charging
     0x00850045 = Discharging
     0x008500d0 = ACPresent
    

    But these values could be different for my UPS model.

    Also I succeeded with connecting to the UPS usb:

    Code:

        QString devicePath = "\\\\?\\HID#VID_....";
        HANDLE fileHandle = CreateFileA(devicePath.toStdString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    
        if (fileHandle != INVALID_HANDLE_VALUE) {
            std::cout << "Success. Device opened for reading..." << std::endl;
            OVERLAPPED overlapped = {};
            uint8_t readBuffer[1024];
            BOOL readDeviceRes = ReadFileEx(fileHandle, (LPVOID)readBuffer, sizeof(readBuffer), &overlapped, readCompleted);
    
            if (readDeviceRes) {
                std::cout << "Success..." << std::endl;
            } else {
                std::cout << "Failed..." << std::endl;
            }
        } else {
            std::cout << "Failed to open device for reading!" << std::endl;
        }
    
        CloseHandle(fileHandle);
    
    void CALLBACK readCompleted(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
    {
        std::cout << "Test... " << dwNumberOfBytesTransfered << std::endl;
    }
    

    When I start the app, it displays:

    Success. Device opened for reading...
    Success...

    But ViewPower app displays that UPS connection has been lost, when I close my app, then the UPS connection has been established. This means that they also read the UPS data from a USB. Now I need to figure out how to get these values, read bytes.



  • I have found that I need to use the HID API to get some values for the UPS.

    Updated code:

       QString devicePath = "\\\\?\\HID#VID_....";
       HANDLE fileHandle = CreateFileA(devicePath.toStdString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    
       if (fileHandle != INVALID_HANDLE_VALUE) {
           std::cout << "Success. Device opened for reading..." << std::endl;
           PHIDP_PREPARSED_DATA preparsedData;
           HIDP_CAPS capabilities;
           HIDD_ATTRIBUTES attributes;
           BOOL hidPreparsedRes = HidD_GetPreparsedData(fileHandle, &preparsedData);
    
           if (hidPreparsedRes) {
               if (HidD_GetAttributes(fileHandle, &attributes)) {
                   std::cout << "Product ID: " << attributes.ProductID << std::endl;
                   std::cout << "Size: " << attributes.Size << std::endl;
                   std::cout << "Vendor ID: " << attributes.VendorID << std::endl;
                   std::cout << "Version number: " << attributes.VersionNumber << std::endl;
    
                  if (HidP_GetCaps(preparsedData, &capabilities) == HIDP_STATUS_SUCCESS) {
                      std::cout << "Caps: " << capabilities.NumberOutputValueCaps << std::endl;
                  } else {
                      std::cout << "Failed to return HID capabilities!" << std::endl;
                  }
               } else {
                  std::cout << "Failed to get HID attributes" << std::endl;
               }
    
               std::cout << getLastErrorAsString() << std::endl;
           } else {
               std::cout << "Failed to get preparsed data!" << std::endl;
           }
    
           HidD_FreePreparsedData(preparsedData);
       } else {
           std::cout << "Failed to open device for reading!" << std::endl;
       }
    
       CloseHandle(fileHandle);
    

    So now, it returns:

    Success. Device opened for reading...
    Product ID: 20833
    Size: 12
    Vendor ID: 1637
    Version number: 2
    Caps: 1

    HIDP_CAPS structure has a lot of different values, the question is how to convert them to UPS values to check for power changes/UPS status? Thanks.


Log in to reply