{
	var info types.MachineInfo
	var chassisInfo types.ChassisInfo
	const sysfsDmiPath = "/sys/class/dmi/id"
	productName, err := readSysFile(sysfsDmiPath, "product_name")
	if err != nil {
		info.ProductName = ""
	} else {
		info.ProductName = strings.TrimSpace(productName)
	}
	manufacturer, err := readSysFile(sysfsDmiPath, "sys_vendor")
	if err != nil {
		info.Manufacturer = "Unknown"
	} else {
		info.Manufacturer = strings.TrimSpace(manufacturer)
	}
	version, err := readSysFile(sysfsDmiPath, "product_version")
	if err != nil {
		info.Version = ""
	} else {
		info.Version = strings.TrimSpace(version)
	}
	// Chassis information
	chassisType, err := readSysFile(sysfsDmiPath, "chassis_type")
	if err != nil {
		chassisInfo.ID = -1
		chassisInfo.Type = types.UnknownChassis
	} else {
		chassisInfo.ID, err = strconv.Atoi(strings.TrimSpace(chassisType))
		if err != nil {
			return info, err
		}
		chassisInfo.Type = MapChassisType(chassisInfo.ID)
	}
	chassisVendor, err := readSysFile(sysfsDmiPath, "chassis_vendor")
	if err != nil {
		chassisInfo.Manufacturer = "Unknown"
	} else {
		chassisInfo.Manufacturer = strings.TrimSpace(chassisVendor)
	}
	chassisVersion, err := readSysFile(sysfsDmiPath, "chassis_version")
	if err != nil {
		chassisInfo.Version = "Unknown"
	} else {
		chassisInfo.Version = strings.TrimSpace(chassisVersion)
	}
	info.Chassis = chassisInfo
	// BIOS information
	biosVendor, err := readSysFile(sysfsDmiPath, "bios_vendor")
	if err != nil {
		info.Bios.Vendor = "Unknown"
	} else {
		info.Bios.Vendor = strings.TrimSpace(biosVendor)
	}
	biosVersion, err := readSysFile(sysfsDmiPath, "bios_version")
	if err != nil {
		info.Bios.Version = "Unknown"
	} else {
		info.Bios.Version = strings.TrimSpace(biosVersion)
	}
	biosRelease, err := readSysFile(sysfsDmiPath, "bios_release")
	if err != nil {
		info.Bios.Release = "Unknown"
	} else {
		info.Bios.Release = strings.TrimSpace(biosRelease)
	}
	// Board information
	boardName, err := readSysFile(sysfsDmiPath, "board_name")
	if err != nil {
		info.Board.ProductName = "Unknown"
	} else {
		info.Board.ProductName = strings.TrimSpace(boardName)
	}
	boardVendor, err := readSysFile(sysfsDmiPath, "board_vendor")
	if err != nil {
		info.Board.Manufacturer = "Unknown"
	} else {
		info.Board.Manufacturer = strings.TrimSpace(boardVendor)
	}
	boardVersion, err := readSysFile(sysfsDmiPath, "board_version")
	if err != nil {
		info.Board.Version = "Unknown"
	} else {
		info.Board.Version = strings.TrimSpace(boardVersion)
	}
	return info, nil
}MapChassisType maps the chassis type to a standardized representation.
Refer to https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.7.0.pdf
section 7.4.1 (System Enclosure or Chassis Types) and
https://superuser.com/a/1107191 for more information.
{
	switch chassisTypeID {
	case 1:
		return types.OtherChassis
	case 2:
		return types.UnknownChassis
	case 3:
		return types.DesktopChassis
	case 4:
		return types.LowProfileDesktop
	case 5:
		return types.PizzaBoxChassis
	case 6:
		return types.MiniTowerChassis
	case 7:
		return types.TowerChassis
	case 8:
		return types.PortableChassis
	case 9:
		return types.LaptopChassis
	case 10:
		return types.NotebookChassis
	case 11:
		return types.HandHeldChassis
	case 12:
		return types.DockingStation
	case 13:
		return types.AllInOneChassis
	case 14:
		return types.SubNotebookChassis
	case 15:
		return types.SpaceSavingChassis
	case 16:
		return types.LunchBoxChassis
	case 17:
		return types.MainServerChassis
	case 18:
		return types.ExpansionChassis
	case 19:
		return types.SubChassis
	case 20:
		return types.BusExpansionChassis
	case 21:
		return types.PeripheralChassis
	case 22:
		return types.RAIDChassis
	case 23:
		return types.RackMountChassis
	case 24:
		return types.SealedCasePC
	case 25:
		return types.MultiSystemChassis
	case 26:
		return types.CompactPCIChassis
	case 27:
		return types.AdvancedTCAChassis
	case 28:
		return types.BladeChassis
	case 29:
		return types.BladeEnclosureChassis
	case 30:
		return types.TabletChassis
	case 31:
		return types.ConvertibleChassis
	case 32:
		return types.DetachableChassis
	case 33:
		return types.IoTGatewayChassis
	case 34:
		return types.EmbeddedPCChassis
	case 35:
		return types.MiniPCChassis
	case 36:
		return types.StickPCChassis
	default:
		return types.UnknownChassis
	}
}GetBatteryStats retrieves battery statistics using sysfs. If the battery
capacity information is not available, it returns nil assuming it's not a
portable device.
batteryStats, err := hardware.GetBatteryStats()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Percentage: %d\n", batteryStats.Percentage)
fmt.Printf("Status: %s\n", batteryStats.Status)
fmt.Printf("Voltage: %d\n", batteryStats.Voltage)
{
	slot, err := getBatterySlot()
	if err != nil {
		// If battery slot is not found, assume it's not a portable device
		return nil, nil
	}
	sysfsBatteryPath := "/sys/class/power_supply/" + slot
	capacityContent, err := readSysFile(sysfsBatteryPath, "charge_full")
	if err != nil {
		return nil, fmt.Errorf("failed to read battery capacity: %v", err)
	}
	capacity, err := strconv.Atoi(strings.TrimSpace(capacityContent))
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery capacity: %v", err)
	}
	capacityDesignContent, err := readSysFile(sysfsBatteryPath, "charge_full_design")
	if err != nil {
		return nil, fmt.Errorf("failed to read battery design capacity: %v", err)
	}
	capacityDesign, err := strconv.Atoi(strings.TrimSpace(capacityDesignContent))
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery design capacity: %v", err)
	}
	percentageContent, err := readSysFile(sysfsBatteryPath, "capacity")
	if err != nil {
		// If battery capacity information is not available, assume it's not
		// a portable device
		return nil, nil
	}
	percentage, err := strconv.Atoi(strings.TrimSpace(percentageContent))
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery percentage: %v", err)
	}
	statusContent, err := readSysFile(sysfsBatteryPath, "status")
	if err != nil {
		return nil, fmt.Errorf("failed to read battery status: %v", err)
	}
	var status types.BatteryStatus
	switch strings.ToLower(strings.TrimSpace(statusContent)) {
	case "not charging":
		status = types.BatteryStatusNotCharging
	case "charging":
		status = types.BatteryStatusCharging
	case "discharging":
		status = types.BatteryStatusDischarging
	case "full":
		status = types.BatteryStatusFull
	default:
		status = types.BatteryStatusUnknown
	}
	voltageContent, err := readSysFile(sysfsBatteryPath, "voltage_now")
	if err != nil {
		return nil, fmt.Errorf("failed to read battery voltage: %v", err)
	}
	voltage, err := strconv.Atoi(strings.TrimSpace(voltageContent))
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery voltage: %v", err)
	}
	batteryStats := &types.BatteryStats{
		Capacity:       capacity,
		CapacityDesign: capacityDesign,
		Percentage:     percentage,
		Status:         status,
		Voltage:        voltage,
	}
	return batteryStats, nil
}GetBatteryHealth calculates the battery health based on the battery
statistics. It returns the battery health percentage.
batteryStats, err := hardware.GetBatteryStats()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
batteryHealth, err := hardware.GetBatteryHealth(batteryStats)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Health: %f\n", batteryHealth)
{
	health := (float64(batteryStats.Capacity) / float64(batteryStats.CapacityDesign)) * 100
	if health > 100 {
		health = 100
	}
	return health, nil
}getBatterySlot returns the available battery slot.
{
	const sysfsPowerSupplyPath = "/sys/class/power_supply"
	files, err := os.ReadDir(sysfsPowerSupplyPath)
	if err != nil {
		return "", fmt.Errorf("failed to read power supply directory: %v", err)
	}
	for _, file := range files {
		if strings.HasPrefix(file.Name(), "BAT") {
			return file.Name(), nil
		}
	}
	return "", fmt.Errorf("no battery slot found")
}GetPeripheralList returns a list of all system peripherals.
peripherals, err := hardware.GetPeripheralList()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
for _, peripheral := range peripherals {
	fmt.Printf("ID: %s\n", peripheral.ID)
	fmt.Printf("Name: %s\n", peripheral.Name)
	fmt.Printf("Type: %s\n", peripheral.Type)
}
{
	peripherals := make([]types.Peripheral, 0)
	inputDevices, err := GetInputDevices()
	if err != nil {
		return nil, err
	}
	for _, inputDevice := range inputDevices {
		peripherals = append(peripherals, types.Peripheral{
			ID:   inputDevice.Product,
			Name: inputDevice.Name,
			Type: types.InputDeviceType,
		})
	}
	pciDevices, err := GetPCIDevices()
	if err != nil {
		return nil, err
	}
	for _, pciDevice := range pciDevices {
		peripherals = append(peripherals, types.Peripheral{
			ID:   pciDevice.ID,
			Name: pciDevice.Name,
			Type: types.PCIDeviceType,
		})
	}
	return peripherals, nil
}GetInputDevices returns a list of input devices with specific details.
inputDevices, err := hardware.GetInputDevices()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
for _, inputDevice := range inputDevices {
	fmt.Printf("Name: %s\n", inputDevice.Name)
	fmt.Printf("Product: %s\n", inputDevice.Product)
}
{
	inputDevices := make([]types.InputDevice, 0)
	inputDevicePaths, err := getInputDevicePaths()
	if err != nil {
		return nil, err
	}
	for _, path := range inputDevicePaths {
		deviceInfo, err := readInputDeviceInfo(path)
		if err != nil {
			return nil, err
		}
		inputDevices = append(inputDevices, deviceInfo)
	}
	return inputDevices, nil
}getInputDevicePaths returns a list of paths for input devices.
{
	var inputDevicePaths []string
	files, err := os.ReadDir("/sys/class/input")
	if err != nil {
		return nil, fmt.Errorf("error reading /sys/class/input: %v", err)
	}
	for _, file := range files {
		if strings.HasPrefix(file.Name(), "event") {
			inputDevicePaths = append(inputDevicePaths, fmt.Sprintf("/sys/class/input/%s/device", file.Name()))
		}
	}
	return inputDevicePaths, nil
}readInputDeviceInfo reads specific information about an input device.
{
	deviceInfo := types.InputDevice{}
	ueventPath := fmt.Sprintf("%s/uevent", devicePath)
	file, err := os.Open(ueventPath)
	if err != nil {
		return deviceInfo, fmt.Errorf("error opening %s: %v", ueventPath, err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "PRODUCT=") {
			deviceInfo.Product = strings.TrimPrefix(line, "PRODUCT=")
		} else if strings.HasPrefix(line, "NAME=") {
			deviceInfo.Name = strings.TrimPrefix(line, "NAME=")
		}
	}
	return deviceInfo, nil
}GetPCIDevices returns a list of PCI devices from /sys/bus/pci/devices.
pciDevices, err := hardware.GetPCIDevices()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
for _, pciDevice := range pciDevices {
	fmt.Printf("ID: %s\n", pciDevice.ID)
}
{
	pciDevices := make([]types.PCIDevice, 0)
	files, err := os.ReadDir("/sys/bus/pci/devices")
	if err != nil {
		return nil, fmt.Errorf("error reading /sys/bus/pci/devices: %v", err)
	}
	for _, file := range files {
		pciDevice := types.PCIDevice{
			ID: file.Name(),
		}
		devicePath := fmt.Sprintf("/sys/bus/pci/devices/%s", file.Name())
		if vendor, err := readSysFile(devicePath, "vendor"); err == nil {
			pciDevice.Vendor = vendor
		}
		if device, err := readSysFile(devicePath, "device"); err == nil {
			pciDevice.Device = device
		}
		if class, err := readSysFile(devicePath, "class"); err == nil {
			pciDevice.Class = class
		}
		if subsystemDevice, err := readSysFile(devicePath, "subsystem_device"); err == nil {
			pciDevice.SubsystemDevice = subsystemDevice
		}
		if subsystemVendor, err := readSysFile(devicePath, "subsystem_vendor"); err == nil {
			pciDevice.SubsystemVendor = subsystemVendor
		}
		if modalias, err := readSysFile(devicePath, "modalias"); err == nil {
			pciDevice.Modalias = modalias
		}
		if device, vendorName, err := GetPCIDeviceByIDs(pciDevice.Vendor, pciDevice.Device); err == nil {
			pciDevice.Name = device.Name
			pciDevice.VendorName = vendorName
		}
		pciDevices = append(pciDevices, pciDevice)
	}
	return pciDevices, nil
}GetPCIDeviceByIDs returns a PCIDeviceMapDevice by vendor ID and device ID,
useful for getting the name of a PCI device or the vendor name. It returns
an error if the device is not found.
device, vendorName, err := hardware.GetPCIDeviceByIDs("8086", "10f8")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Device name: %s\n", device.Name)
fmt.Printf("Vendor name: %s\n", vendorName)
{
	// Cleanup the vendor ID and device ID
	vendorID = strings.TrimPrefix(vendorID, "0x")
	deviceID = strings.TrimPrefix(deviceID, "0x")
	for _, vendor := range pciDeviceMap {
		if vendor.ID == vendorID {
			for _, device := range vendor.Devices {
				if device.ID == deviceID {
					return device, vendor.Name, nil
				}
			}
		}
	}
	return types.PCIDeviceMapDevice{}, "", fmt.Errorf("device not found for vendor ID: %s and device ID: %s", vendorID, deviceID)
}readSysFile reads the content of a file in the /sys directory.
{
	filePath := fmt.Sprintf("%s/%s", devicePath, fileName)
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("error opening %s: %v", filePath, err)
	}
	defer file.Close()
	content, err := io.ReadAll(file)
	if err != nil {
		return "", fmt.Errorf("error reading %s: %v", filePath, err)
	}
	return strings.TrimSpace(string(content)), nil
}LoadPCIDeviceMap loads the PCI IDs from /usr/share/misc/pci.ids. This function
should be called at the beginning of the program or before using any other
function from this package.
# File syntax Syntax:
# vendor vendor_name
# device device_name <-- single tab
# subvendor subdevice subsystem_name <-- two tabs
# ...
err := hardware.Loadtypes.PCIDeviceMap()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
Subdevice is not supported yet.
{
	pciDeviceMap = make(types.PCIDeviceMap, 0)
	pciIDsData, err := os.ReadFile("/usr/share/misc/pci.ids")
	if err != nil {
		// @sdk:hint
		fmt.Println("Warning: /usr/share/misc/pci.ids not found, using embedded data assuming we are in a development environment.")
		pciIDsData = []byte(pciIDsData)
	}
	scanner := bufio.NewScanner(strings.NewReader(string(pciIDsData)))
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "#") {
			continue
		}
		if line == "" {
			continue
		}
		if !strings.HasPrefix(line, "\t") {
			// Vendor
			parts := strings.Split(line, "  ")
			currentVendor := types.PCIDeviceMapVendor{
				ID:   parts[0],
				Name: parts[1],
			}
			pciDeviceMap = append(pciDeviceMap, currentVendor)
		} else if strings.HasPrefix(line, "\t\t") {
			// Subdevice not supported yet
			continue
		} else {
			// Device
			parts := strings.Split(line, "  ")
			if len(parts) != 2 {
				continue
			}
			id := strings.TrimSpace(parts[0])
			name := strings.TrimSpace(parts[1])
			currentDevice := types.PCIDeviceMapDevice{
				ID:   id,
				Name: name,
			}
			pciDeviceMap[len(pciDeviceMap)-1].Devices = append(pciDeviceMap[len(pciDeviceMap)-1].Devices, currentDevice)
		}
	}
	return nil
}import "strconv"import "strings"import "github.com/vanilla-os/sdk/pkg/v1/hardware/types"import "fmt"import "os"import "strconv"import "strings"import "github.com/vanilla-os/sdk/pkg/v1/hardware/types"import "bufio"import "embed"Anonymous Import
import "fmt"import "io"import "os"import "strings"import "github.com/vanilla-os/sdk/pkg/v1/hardware/types"