<artifactId>org.openhab.binding.systeminfo</artifactId>
- <name>openHAB Add-ons :: Bundles :: Systeminfo Binding</name>
+ <name>openHAB Add-ons :: Bundles :: SystemInfo Binding</name>
<dependencies>
<dependency>
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SystemInfoBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Mark Herwege - Add dynamic creation of extra channels
+ * @author Mark Herwege - Processor frequency channels
+ */
+@NonNullByDefault
+public class SystemInfoBindingConstants {
+
+ public static final String BINDING_ID = "systeminfo";
+
+ public static final String THING_TYPE_COMPUTER_ID = "computer";
+ public static final ThingTypeUID THING_TYPE_COMPUTER = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID);
+
+ // Thing properties
+ /**
+ * Number of CPU logical cores
+ */
+ public static final String PROPERTY_CPU_LOGICAL_CORES = "CPU Logical Cores";
+
+ /**
+ * Number of CPU physical cores
+ */
+ public static final String PROPERTY_CPU_PHYSICAL_CORES = "CPU Physical Cores";
+
+ /**
+ * Contains information about the family /Windows, Linux, OS X etc/ of the operation system
+ */
+ public static final String PROPERTY_OS_FAMILY = "OS Family";
+
+ /**
+ * Name of the manufacturer of the operation system
+ */
+ public static final String PROPERTY_OS_MANUFACTURER = "OS Manufacturer";
+
+ /**
+ * Version of the operation system
+ */
+ public static final String PROPERTY_OS_VERSION = "OS Version";
+
+ // List of all Channel IDs
+
+ /**
+ * Name of the channel group type for memory information
+ */
+ public static final String CHANNEL_GROUP_TYPE_MEMORY = "memoryGroup";
+
+ /**
+ * Name of the channel group for memory information
+ */
+ public static final String CHANNEL_GROUP_MEMORY = "memory";
+
+ /**
+ * Size of the available memory
+ */
+ public static final String CHANNEL_MEMORY_AVAILABLE = "memory#available";
+
+ /**
+ * Size of the used memory
+ */
+ public static final String CHANNEL_MEMORY_USED = "memory#used";
+
+ /**
+ * Total size of the memory
+ */
+ public static final String CHANNEL_MEMORY_TOTAL = "memory#total";
+
+ /**
+ * Percents of the available memory
+ */
+ public static final String CHANNEL_MEMORY_AVAILABLE_PERCENT = "memory#availablePercent";
+
+ /**
+ * Percents of the used memory
+ */
+ public static final String CHANNEL_MEMORY_USED_PERCENT = "memory#usedPercent";
+
+ /**
+ * Percents of the used heap
+ */
+ public static final String CHANNEL_MEMORY_USED_HEAP_PERCENT = "memory#usedHeapPercent";
+
+ /**
+ * Bytes used in the heap
+ */
+ public static final String CHANNEL_MEMORY_HEAP_AVAILABLE = "memory#availableHeap";
+
+ /**
+ * Name of the channel group type for swap information
+ */
+ public static final String CHANNEL_GROUP_TYPE_SWAP = "swapGroup";
+
+ /**
+ * Name of the channel group for swap information
+ */
+ public static final String CHANNEL_GROUP_SWAP = "swap";
+
+ /**
+ * Total size of swap memory
+ */
+ public static final String CHANNEL_SWAP_TOTAL = "swap#total";
+
+ /**
+ * Size of the available swap memory
+ */
+ public static final String CHANNEL_SWAP_AVAILABLE = "swap#available";
+
+ /**
+ * Size of the used swap memory
+ */
+ public static final String CHANNEL_SWAP_USED = "swap#used";
+
+ /**
+ * Percents of the available swap memory
+ */
+ public static final String CHANNEL_SWAP_AVAILABLE_PERCENT = "swap#availablePercent";
+
+ /**
+ * Percents of the used swap memory
+ */
+ public static final String CHANNEL_SWAP_USED_PERCENT = "swap#usedPercent";
+
+ /**
+ * Name of the channel group type for drive information
+ */
+ public static final String CHANNEL_GROUP_TYPE_DRIVE = "driveGroup";
+
+ /**
+ * Name of the channel group for drive information
+ */
+ public static final String CHANNEL_GROUP_DRIVE = "drive";
+
+ /**
+ * Physical storage drive name
+ */
+ public static final String CHANNEL_DRIVE_NAME = "drive#name";
+
+ /**
+ * Physical storage drive model
+ */
+ public static final String CHANNEL_DRIVE_MODEL = "drive#model";
+
+ /**
+ * Physical storage drive serial number
+ */
+ public static final String CHANNEL_DRIVE_SERIAL = "drive#serial";
+
+ /**
+ * Name of the channel group type for storage information
+ */
+ public static final String CHANNEL_GROUP_TYPE_STORAGE = "storageGroup";
+
+ /**
+ * Name of the channel group for storage information
+ */
+ public static final String CHANNEL_GROUP_STORAGE = "storage";
+
+ /**
+ * Name of the logical volume storage
+ */
+ public static final String CHANNEL_STORAGE_NAME = "storage#name";
+
+ /**
+ * Logical storage volume type -(e.g. NTFS, FAT32 ..)
+ */
+ public static final String CHANNEL_STORAGE_TYPE = "storage#type";
+
+ /**
+ * Description of the logical volume storage
+ */
+ public static final String CHANNEL_STORAGE_DESCRIPTION = "storage#description";
+
+ /**
+ * Size of the available storage space
+ */
+ public static final String CHANNEL_STORAGE_AVAILABLE = "storage#available";
+
+ /**
+ * Size of the used storage space
+ */
+ public static final String CHANNEL_STORAGE_USED = "storage#used";
+
+ /**
+ * Total storage space
+ */
+ public static final String CHANNEL_STORAGE_TOTAL = "storage#total";
+
+ /**
+ * Percents of the available storage space
+ */
+ public static final String CHANNEL_STORAGE_AVAILABLE_PERCENT = "storage#availablePercent";
+
+ /**
+ * Percents of the used storage space
+ */
+ public static final String CHANNEL_STORAGE_USED_PERCENT = "storage#usedPercent";
+
+ /**
+ * Name of the channel group type for sensors information
+ */
+ public static final String CHANNEL_GROUP_TYPE_SENSORS = "sensorsGroup";
+
+ /**
+ * Name of the channel group for sensors information
+ */
+ public static final String CHANNEL_GROUP_SENSORS = "sensors";
+
+ /**
+ * Temperature of the CPU measured from the sensors.
+ */
+ public static final String CHANNEL_SENSORS_CPU_TEMPERATURE = "sensors#cpuTemp";
+
+ /**
+ * Voltage of the CPU core.
+ */
+ public static final String CHANNEL_SENOSRS_CPU_VOLTAGE = "sensors#cpuVoltage";
+
+ /**
+ * Fan speed
+ */
+ public static final String CHANNEL_SENSORS_FAN_SPEED = "sensors#fanSpeed";
+
+ /**
+ * Name of the channel group type for battery information
+ */
+ public static final String CHANNEL_GROUP_TYPE_BATTERY = "batteryGroup";
+
+ /**
+ * Name of the channel group for battery information
+ */
+ public static final String CHANNEL_GROUP_BATTERY = "battery";
+
+ /**
+ * Name of the battery
+ */
+ public static final String CHANNEL_BATTERY_NAME = "battery#name";
+
+ /**
+ * Remaining capacity of the battery.
+ */
+ public static final String CHANNEL_BATTERY_REMAINING_CAPACITY = "battery#remainingCapacity";
+
+ /**
+ * Estimated remaining time of the battery
+ */
+ public static final String CHANNEL_BATTERY_REMAINING_TIME = "battery#remainingTime";
+
+ /**
+ * Name of the channel group type for CPU information
+ */
+ public static final String CHANNEL_GROUP_TYPE_CPU = "cpuGroup";
+
+ /**
+ * Name of the channel group for CPU information
+ */
+ public static final String CHANNEL_GROUP_CPU = "cpu";
+
+ /**
+ * Detailed description about the CPU
+ */
+ public static final String CHANNEL_CPU_DESCRIPTION = "cpu#description";
+
+ /**
+ * Maximum frequency of the CPU
+ */
+ public static final String CHANNEL_CPU_MAXFREQ = "cpu#maxfreq";
+
+ /**
+ * Frequency of the CPU
+ */
+ public static final String CHANNEL_CPU_FREQ = "cpu#freq";
+
+ /**
+ * Average recent CPU load
+ */
+ public static final String CHANNEL_CPU_LOAD = "cpu#load";
+
+ /**
+ * Average CPU load for the last minute
+ */
+ public static final String CHANNEL_CPU_LOAD_1 = "cpu#load1";
+
+ /**
+ * Average CPU load for the last 5 minutes
+ */
+ public static final String CHANNEL_CPU_LOAD_5 = "cpu#load5";
+
+ /**
+ * Average CPU load for the last 15 minutes
+ */
+ public static final String CHANNEL_CPU_LOAD_15 = "cpu#load15";
+
+ /**
+ * CPU name
+ */
+ public static final String CHANNEL_CPU_NAME = "cpu#name";
+
+ /**
+ * CPU uptime in minutes
+ */
+ public static final String CHANNEL_CPU_UPTIME = "cpu#uptime";
+
+ /**
+ * CPU running threads count
+ */
+ public static final String CHANNEL_CPU_THREADS = "cpu#threads";
+
+ /**
+ * Name of the channel group type for display information
+ */
+ public static final String CHANNEL_GROUP_TYPE_DISPLAY = "displayGroup";
+
+ /**
+ * Name of the channel group for display information
+ */
+ public static final String CHANNEL_GROUP_DISPLAY = "display";
+
+ /**
+ * Information about the display device
+ */
+ public static final String CHANNEL_DISPLAY_INFORMATION = "display#information";
+
+ /**
+ * Name of the channel group type for network information
+ */
+ public static final String CHANNEL_GROUP_TYPE_NETWORK = "networkGroup";
+
+ /**
+ * Name of the channel group for network information
+ */
+ public static final String CHANNEL_GROUP_NETWORK = "network";
+
+ /**
+ * Host IP address of the network
+ */
+ public static final String CHANNEL_NETWORK_IP = "network#ip";
+
+ /**
+ * Network display name
+ */
+ public static final String CHANNEL_NETWORK_ADAPTER_NAME = "network#networkName";
+
+ /**
+ * Network data sent
+ */
+ public static final String CHANNEL_NETWORK_DATA_SENT = "network#dataSent";
+
+ /**
+ * Network data received
+ */
+ public static final String CHANNEL_NETWORK_DATA_RECEIVED = "network#dataReceived";
+
+ /**
+ * Network packets sent
+ */
+ public static final String CHANNEL_NETWORK_PACKETS_SENT = "network#packetsSent";
+
+ /**
+ * Network packets received
+ */
+ public static final String CHANNEL_NETWORK_PACKETS_RECEIVED = "network#packetsReceived";
+
+ /**
+ * Network name
+ */
+ public static final String CHANNEL_NETWORK_NAME = "network#networkDisplayName";
+
+ /**
+ * Network mac address
+ */
+ public static final String CHANNEL_NETWORK_MAC = "network#mac";
+
+ /**
+ * Name of the channel group type for process information
+ */
+ public static final String CHANNEL_GROUP_TYPE_CURRENT_PROCESS = "currentProcessGroup";
+
+ /**
+ * Name of the channel group for process information
+ */
+ public static final String CHANNEL_GROUP_CURRENT_PROCESS = "currentProcess";
+
+ /**
+ * CPU load used from a process
+ */
+
+ public static final String CHANNEL_CURRENT_PROCESS_LOAD = "currentProcess#load";
+
+ /**
+ * Size of memory used from a process in MB
+ */
+ public static final String CHANNEL_CURRENT_PROCESS_MEMORY = "currentProcess#used";
+
+ /**
+ * Name of the process
+ */
+ public static final String CHANNEL_CURRENT_PROCESS_NAME = "currentProcess#name";
+
+ /**
+ * Number of threads, used form the process
+ */
+ public static final String CHANNEL_CURRENT_PROCESS_THREADS = "currentProcess#threads";
+
+ /**
+ * The full path of the process
+ */
+ public static final String CHANNEL_CURRENT_PROCESS_PATH = "currentProcess#path";
+
+ /**
+ * Name of the channel group type for process information
+ */
+ public static final String CHANNEL_GROUP_TYPE_PROCESS = "processGroup";
+
+ /**
+ * Name of the channel group for process information
+ */
+ public static final String CHANNEL_GROUP_PROCESS = "process";
+
+ /**
+ * CPU load used from a process
+ */
+
+ public static final String CHANNEL_PROCESS_LOAD = "process#load";
+
+ /**
+ * Size of memory used from a process in MB
+ */
+ public static final String CHANNEL_PROCESS_MEMORY = "process#used";
+
+ /**
+ * Name of the process
+ */
+ public static final String CHANNEL_PROCESS_NAME = "process#name";
+
+ /**
+ * Number of threads, used form the process
+ */
+ public static final String CHANNEL_PROCESS_THREADS = "process#threads";
+
+ /**
+ * The full path of the process
+ */
+ public static final String CHANNEL_PROCESS_PATH = "process#path";
+
+ // Thing configuraion
+ /**
+ * Name of the configuration parameter of the thing that defines refresh time for High priority channels
+ */
+ public static final String HIGH_PRIORITY_REFRESH_TIME = "interval_high";
+
+ /**
+ * Name of the configuration parameter of the thing that defines refresh time for Medium priority channels
+ */
+ public static final String MEDIUM_PRIORITY_REFRESH_TIME = "interval_medium";
+
+ // Channel configuration
+
+ /**
+ * Name of the channel configuration parameter priority
+ */
+ public static final String PRIOIRITY_PARAM = "priority";
+
+ /**
+ * Name of the channel configuration parameter pid
+ *
+ */
+ public static final String PID_PARAM = "pid";
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal;
+
+import static org.openhab.binding.systeminfo.internal.SystemInfoBindingConstants.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.systeminfo.internal.handler.SystemInfoHandler;
+import org.openhab.binding.systeminfo.internal.model.SystemInfoInterface;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link SystemInfoHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Lyubomir Papazov - Pass systeminfo service to the SystemInfoHandler constructor
+ * @author Wouter Born - Add null annotations
+ * @author Mark Herwege - Add dynamic creation of extra channels
+ */
+@NonNullByDefault
+@Component(service = ThingHandlerFactory.class, configurationPid = "binding.systeminfo")
+public class SystemInfoHandlerFactory extends BaseThingHandlerFactory {
+ private @NonNullByDefault({}) SystemInfoInterface systeminfo;
+ private @NonNullByDefault({}) SystemInfoThingTypeProvider thingTypeProvider;
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return BINDING_ID.equals(thingTypeUID.getBindingId())
+ && thingTypeUID.getId().startsWith(THING_TYPE_COMPUTER_ID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+ if (supportsThingType(thingTypeUID)) {
+ String extString = "-" + thing.getUID().getId();
+ ThingTypeUID extThingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID + extString);
+ if (thingTypeProvider.getThingType(extThingTypeUID, null) == null) {
+ thingTypeProvider.createThingType(extThingTypeUID);
+ thingTypeProvider.storeChannelsConfig(thing); // Save the current channels configs, will be restored
+ // after thing type change.
+ }
+ return new SystemInfoHandler(thing, thingTypeProvider, systeminfo);
+ }
+ return null;
+ }
+
+ @Reference
+ public void bindSystemInfo(SystemInfoInterface systeminfo) {
+ this.systeminfo = systeminfo;
+ }
+
+ public void unbindSystemInfo(SystemInfoInterface systeminfo) {
+ this.systeminfo = null;
+ }
+
+ @Reference
+ public void setSystemInfoThingTypeProvider(SystemInfoThingTypeProvider thingTypeProvider) {
+ this.thingTypeProvider = thingTypeProvider;
+ }
+
+ public void unsetSystemInfoThingTypeProvider(SystemInfoThingTypeProvider thingTypeProvider) {
+ this.thingTypeProvider = null;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal;
+
+import static org.openhab.binding.systeminfo.internal.SystemInfoBindingConstants.*;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.storage.StorageService;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider;
+import org.openhab.core.thing.binding.ThingTypeProvider;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.type.ChannelGroupDefinition;
+import org.openhab.core.thing.type.ChannelGroupType;
+import org.openhab.core.thing.type.ChannelGroupTypeRegistry;
+import org.openhab.core.thing.type.ChannelGroupTypeUID;
+import org.openhab.core.thing.type.ChannelType;
+import org.openhab.core.thing.type.ChannelTypeRegistry;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.thing.type.ThingType;
+import org.openhab.core.thing.type.ThingTypeBuilder;
+import org.openhab.core.thing.type.ThingTypeRegistry;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended channels can be auto discovered and added to newly created groups in the
+ * {@link org.openhab.binding.systeminfo.internal.handler.SystemInfoHandler}. The
+ * thing needs to be updated to add the groups. The `SystemInfoThingTypeProvider` OSGi service gives access to the
+ * `ThingTypeRegistry` and serves the updated `ThingType`.
+ *
+ * @author Mark Herwege - Initial contribution
+ *
+ */
+@NonNullByDefault
+@Component(service = { SystemInfoThingTypeProvider.class, ThingTypeProvider.class })
+public class SystemInfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
+ private final Logger logger = LoggerFactory.getLogger(SystemInfoThingTypeProvider.class);
+
+ private final ThingTypeRegistry thingTypeRegistry;
+ private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
+ private final ChannelTypeRegistry channelTypeRegistry;
+
+ private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
+
+ @Activate
+ public SystemInfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
+ @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
+ @Reference ChannelTypeRegistry channelTypeRegistry, @Reference StorageService storageService) {
+ super(storageService);
+ this.thingTypeRegistry = thingTypeRegistry;
+ this.channelGroupTypeRegistry = channelGroupTypeRegistry;
+ this.channelTypeRegistry = channelTypeRegistry;
+ }
+
+ /**
+ * Create thing type with the provided typeUID and add it to the thing type registry.
+ *
+ * @param typeUID
+ * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
+ */
+ public boolean createThingType(ThingTypeUID typeUID) {
+ logger.trace("Creating thing type {}", typeUID);
+ return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
+ }
+
+ /**
+ * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
+ *
+ * @param typeUID
+ * @param groupDefs
+ * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
+ */
+ public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
+ ThingType baseType = thingTypeRegistry.getThingType(typeUID);
+ if (baseType == null) {
+ baseType = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
+ if (baseType == null) {
+ logger.warn("Could not find base thing type in registry.");
+ return false;
+ }
+ }
+ ThingTypeBuilder builder = createThingTypeBuilder(typeUID, baseType.getUID());
+ if (builder != null) {
+ logger.trace("Adding channel group definitions to thing type");
+ ThingType type = builder.withChannelGroupDefinitions(groupDefs).build();
+
+ putThingType(type);
+ return true;
+ } else {
+ logger.debug("Error adding channel groups");
+ return false;
+ }
+ }
+
+ /**
+ * Return a {@link ThingTypeBuilder} that can create an exact copy of the `ThingType` with `baseTypeUID`.
+ * Further build steps can be performed on the returned object before recreating the `ThingType` from the builder.
+ *
+ * @param newTypeUID
+ * @param baseTypeUID
+ * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
+ */
+ private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
+ ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
+
+ if (type == null) {
+ return null;
+ }
+
+ ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeUID, type.getLabel())
+ .withChannelGroupDefinitions(type.getChannelGroupDefinitions())
+ .withChannelDefinitions(type.getChannelDefinitions())
+ .withExtensibleChannelTypeIds(type.getExtensibleChannelTypeIds())
+ .withSupportedBridgeTypeUIDs(type.getSupportedBridgeTypeUIDs()).withProperties(type.getProperties())
+ .isListed(false);
+
+ String representationProperty = type.getRepresentationProperty();
+ if (representationProperty != null) {
+ result = result.withRepresentationProperty(representationProperty);
+ }
+ URI configDescriptionURI = type.getConfigDescriptionURI();
+ if (configDescriptionURI != null) {
+ result = result.withConfigDescriptionURI(configDescriptionURI);
+ }
+ String category = type.getCategory();
+ if (category != null) {
+ result = result.withCategory(category);
+ }
+ String description = type.getDescription();
+ if (description != null) {
+ result = result.withDescription(description);
+ }
+
+ return result;
+ }
+
+ /**
+ * Return List of {@link ChannelGroupDefinition} for `ThingType` with `typeUID`. If the `ThingType` does not exist
+ * in the thingTypeRegistry yet, retrieve list of `ChannelGroupDefinition` for base type systeminfo:computer.
+ *
+ * @param typeUID UID for ThingType
+ * @return list of channel group definitions, empty list if no channel group definitions
+ */
+ public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
+ ThingType type = thingTypeRegistry.getThingType(typeUID);
+ if (type == null) {
+ type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
+ }
+ if (type != null) {
+ return type.getChannelGroupDefinitions();
+ } else {
+ logger.debug("Cannot retrieve channel group definitions, no base thing type found");
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Create a new channel group definition with index appended to id and label.
+ *
+ * @param channelGroupID id of channel group without index
+ * @param channelGroupTypeID id ChannelGroupType for new channel group definition
+ * @param i index
+ * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
+ */
+ public @Nullable ChannelGroupDefinition createChannelGroupDefinitionWithIndex(String channelGroupID,
+ String channelGroupTypeID, int i) {
+ ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(BINDING_ID, channelGroupTypeID);
+ ChannelGroupType channelGroupType = channelGroupTypeRegistry.getChannelGroupType(channelGroupTypeUID);
+ if (channelGroupType == null) {
+ logger.debug("Cannot create channel group definition, group type {} invalid", channelGroupTypeID);
+ return null;
+ }
+ String index = String.valueOf(i);
+ return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
+ channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
+ }
+
+ /**
+ * Create a new channel with index appended to id and label of an existing channel.
+ *
+ * @param thing containing the existing channel
+ * @param channelID id of channel without index
+ * @param i index
+ * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
+ * provided channel
+ */
+ public @Nullable Channel createChannelWithIndex(Thing thing, String channelID, int i) {
+ Channel baseChannel = thing.getChannel(channelID);
+ if (baseChannel == null) {
+ logger.debug("Cannot create channel, ID {} invalid", channelID);
+ return null;
+ }
+ ChannelTypeUID channelTypeUID = baseChannel.getChannelTypeUID();
+ ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
+ if (channelType == null) {
+ logger.debug("Cannot create channel, type {} invalid",
+ channelTypeUID != null ? channelTypeUID.getId() : "null");
+ return null;
+ }
+ ThingUID thingUID = thing.getUID();
+ String index = String.valueOf(i);
+ ChannelUID channelUID = new ChannelUID(thingUID, channelID + index);
+ ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
+ .withConfiguration(baseChannel.getConfiguration());
+ builder.withLabel(channelType.getLabel() + " " + index);
+ builder.withDefaultTags(channelType.getTags());
+ String description = channelType.getDescription();
+ if (description != null) {
+ builder.withDescription(description);
+ }
+ String itemType = channelType.getItemType();
+ if (itemType != null) {
+ builder.withAcceptedItemType(itemType);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Store the channel configurations for a thing, to be able to restore them later when the thing handler for the
+ * same thing gets recreated with a new thing type. This is necessary because the
+ * {@link org.openhab.core.thing.binding.BaseThingHandler#changeThingType()} method reverts channel configurations
+ * to their defaults.
+ *
+ * @param thing
+ */
+ public void storeChannelsConfig(Thing thing) {
+ Map<String, Configuration> channelsConfig = thing.getChannels().stream()
+ .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
+ thingChannelsConfig.put(thing.getUID(), channelsConfig);
+ }
+
+ /**
+ * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
+ * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
+ * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
+ * also {@link #storeChannelsConfig(Thing)}.
+ *
+ * @param UID
+ * @return Map of ChannelId and Configuration for the channel
+ */
+ public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
+ Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
+ return configs != null ? configs : Collections.emptyMap();
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link SysteminfoBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Mark Herwege - Add dynamic creation of extra channels
- * @author Mark Herwege - Processor frequency channels
- */
-@NonNullByDefault
-public class SysteminfoBindingConstants {
-
- public static final String BINDING_ID = "systeminfo";
-
- public static final String THING_TYPE_COMPUTER_ID = "computer";
- public static final ThingTypeUID THING_TYPE_COMPUTER = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID);
-
- // Thing properties
- /**
- * Number of CPU logical cores
- */
- public static final String PROPERTY_CPU_LOGICAL_CORES = "CPU Logical Cores";
-
- /**
- * Number of CPU physical cores
- */
- public static final String PROPERTY_CPU_PHYSICAL_CORES = "CPU Physical Cores";
-
- /**
- * Contains information about the family /Windows, Linux, OS X etc/ of the operation system
- */
- public static final String PROPERTY_OS_FAMILY = "OS Family";
-
- /**
- * Name of the manufacturer of the operation system
- */
- public static final String PROPERTY_OS_MANUFACTURER = "OS Manufacturer";
-
- /**
- * Version of the operation system
- */
- public static final String PROPERTY_OS_VERSION = "OS Version";
-
- // List of all Channel IDs
-
- /**
- * Name of the channel group type for memory information
- */
- public static final String CHANNEL_GROUP_TYPE_MEMORY = "memoryGroup";
-
- /**
- * Name of the channel group for memory information
- */
- public static final String CHANNEL_GROUP_MEMORY = "memory";
-
- /**
- * Size of the available memory
- */
- public static final String CHANNEL_MEMORY_AVAILABLE = "memory#available";
-
- /**
- * Size of the used memory
- */
- public static final String CHANNEL_MEMORY_USED = "memory#used";
-
- /**
- * Total size of the memory
- */
- public static final String CHANNEL_MEMORY_TOTAL = "memory#total";
-
- /**
- * Percents of the available memory
- */
- public static final String CHANNEL_MEMORY_AVAILABLE_PERCENT = "memory#availablePercent";
-
- /**
- * Percents of the used memory
- */
- public static final String CHANNEL_MEMORY_USED_PERCENT = "memory#usedPercent";
-
- /**
- * Percents of the used heap
- */
- public static final String CHANNEL_MEMORY_USED_HEAP_PERCENT = "memory#usedHeapPercent";
-
- /**
- * Bytes used in the heap
- */
- public static final String CHANNEL_MEMORY_HEAP_AVAILABLE = "memory#availableHeap";
-
- /**
- * Name of the channel group type for swap information
- */
- public static final String CHANNEL_GROUP_TYPE_SWAP = "swapGroup";
-
- /**
- * Name of the channel group for swap information
- */
- public static final String CHANNEL_GROUP_SWAP = "swap";
-
- /**
- * Total size of swap memory
- */
- public static final String CHANNEL_SWAP_TOTAL = "swap#total";
-
- /**
- * Size of the available swap memory
- */
- public static final String CHANNEL_SWAP_AVAILABLE = "swap#available";
-
- /**
- * Size of the used swap memory
- */
- public static final String CHANNEL_SWAP_USED = "swap#used";
-
- /**
- * Percents of the available swap memory
- */
- public static final String CHANNEL_SWAP_AVAILABLE_PERCENT = "swap#availablePercent";
-
- /**
- * Percents of the used swap memory
- */
- public static final String CHANNEL_SWAP_USED_PERCENT = "swap#usedPercent";
-
- /**
- * Name of the channel group type for drive information
- */
- public static final String CHANNEL_GROUP_TYPE_DRIVE = "driveGroup";
-
- /**
- * Name of the channel group for drive information
- */
- public static final String CHANNEL_GROUP_DRIVE = "drive";
-
- /**
- * Physical storage drive name
- */
- public static final String CHANNEL_DRIVE_NAME = "drive#name";
-
- /**
- * Physical storage drive model
- */
- public static final String CHANNEL_DRIVE_MODEL = "drive#model";
-
- /**
- * Physical storage drive serial number
- */
- public static final String CHANNEL_DRIVE_SERIAL = "drive#serial";
-
- /**
- * Name of the channel group type for storage information
- */
- public static final String CHANNEL_GROUP_TYPE_STORAGE = "storageGroup";
-
- /**
- * Name of the channel group for storage information
- */
- public static final String CHANNEL_GROUP_STORAGE = "storage";
-
- /**
- * Name of the logical volume storage
- */
- public static final String CHANNEL_STORAGE_NAME = "storage#name";
-
- /**
- * Logical storage volume type -(e.g. NTFS, FAT32 ..)
- */
- public static final String CHANNEL_STORAGE_TYPE = "storage#type";
-
- /**
- * Description of the logical volume storage
- */
- public static final String CHANNEL_STORAGE_DESCRIPTION = "storage#description";
-
- /**
- * Size of the available storage space
- */
- public static final String CHANNEL_STORAGE_AVAILABLE = "storage#available";
-
- /**
- * Size of the used storage space
- */
- public static final String CHANNEL_STORAGE_USED = "storage#used";
-
- /**
- * Total storage space
- */
- public static final String CHANNEL_STORAGE_TOTAL = "storage#total";
-
- /**
- * Percents of the available storage space
- */
- public static final String CHANNEL_STORAGE_AVAILABLE_PERCENT = "storage#availablePercent";
-
- /**
- * Percents of the used storage space
- */
- public static final String CHANNEL_STORAGE_USED_PERCENT = "storage#usedPercent";
-
- /**
- * Name of the channel group type for sensors information
- */
- public static final String CHANNEL_GROUP_TYPE_SENSORS = "sensorsGroup";
-
- /**
- * Name of the channel group for sensors information
- */
- public static final String CHANNEL_GROUP_SENSORS = "sensors";
-
- /**
- * Temperature of the CPU measured from the sensors.
- */
- public static final String CHANNEL_SENSORS_CPU_TEMPERATURE = "sensors#cpuTemp";
-
- /**
- * Voltage of the CPU core.
- */
- public static final String CHANNEL_SENOSRS_CPU_VOLTAGE = "sensors#cpuVoltage";
-
- /**
- * Fan speed
- */
- public static final String CHANNEL_SENSORS_FAN_SPEED = "sensors#fanSpeed";
-
- /**
- * Name of the channel group type for battery information
- */
- public static final String CHANNEL_GROUP_TYPE_BATTERY = "batteryGroup";
-
- /**
- * Name of the channel group for battery information
- */
- public static final String CHANNEL_GROUP_BATTERY = "battery";
-
- /**
- * Name of the battery
- */
- public static final String CHANNEL_BATTERY_NAME = "battery#name";
-
- /**
- * Remaining capacity of the battery.
- */
- public static final String CHANNEL_BATTERY_REMAINING_CAPACITY = "battery#remainingCapacity";
-
- /**
- * Estimated remaining time of the battery
- */
- public static final String CHANNEL_BATTERY_REMAINING_TIME = "battery#remainingTime";
-
- /**
- * Name of the channel group type for CPU information
- */
- public static final String CHANNEL_GROUP_TYPE_CPU = "cpuGroup";
-
- /**
- * Name of the channel group for CPU information
- */
- public static final String CHANNEL_GROUP_CPU = "cpu";
-
- /**
- * Detailed description about the CPU
- */
- public static final String CHANNEL_CPU_DESCRIPTION = "cpu#description";
-
- /**
- * Maximum frequency of the CPU
- */
- public static final String CHANNEL_CPU_MAXFREQ = "cpu#maxfreq";
-
- /**
- * Frequency of the CPU
- */
- public static final String CHANNEL_CPU_FREQ = "cpu#freq";
-
- /**
- * Average recent CPU load
- */
- public static final String CHANNEL_CPU_LOAD = "cpu#load";
-
- /**
- * Average CPU load for the last minute
- */
- public static final String CHANNEL_CPU_LOAD_1 = "cpu#load1";
-
- /**
- * Average CPU load for the last 5 minutes
- */
- public static final String CHANNEL_CPU_LOAD_5 = "cpu#load5";
-
- /**
- * Average CPU load for the last 15 minutes
- */
- public static final String CHANNEL_CPU_LOAD_15 = "cpu#load15";
-
- /**
- * CPU name
- */
- public static final String CHANNEL_CPU_NAME = "cpu#name";
-
- /**
- * CPU uptime in minutes
- */
- public static final String CHANNEL_CPU_UPTIME = "cpu#uptime";
-
- /**
- * CPU running threads count
- */
- public static final String CHANNEL_CPU_THREADS = "cpu#threads";
-
- /**
- * Name of the channel group type for display information
- */
- public static final String CHANNEL_GROUP_TYPE_DISPLAY = "displayGroup";
-
- /**
- * Name of the channel group for display information
- */
- public static final String CHANNEL_GROUP_DISPLAY = "display";
-
- /**
- * Information about the display device
- */
- public static final String CHANNEL_DISPLAY_INFORMATION = "display#information";
-
- /**
- * Name of the channel group type for network information
- */
- public static final String CHANNEL_GROUP_TYPE_NETWORK = "networkGroup";
-
- /**
- * Name of the channel group for network information
- */
- public static final String CHANNEL_GROUP_NETWORK = "network";
-
- /**
- * Host IP address of the network
- */
- public static final String CHANNEL_NETWORK_IP = "network#ip";
-
- /**
- * Network display name
- */
- public static final String CHANNEL_NETWORK_ADAPTER_NAME = "network#networkName";
-
- /**
- * Network data sent
- */
- public static final String CHANNEL_NETWORK_DATA_SENT = "network#dataSent";
-
- /**
- * Network data received
- */
- public static final String CHANNEL_NETWORK_DATA_RECEIVED = "network#dataReceived";
-
- /**
- * Network packets sent
- */
- public static final String CHANNEL_NETWORK_PACKETS_SENT = "network#packetsSent";
-
- /**
- * Network packets received
- */
- public static final String CHANNEL_NETWORK_PACKETS_RECEIVED = "network#packetsReceived";
-
- /**
- * Network name
- */
- public static final String CHANNEL_NETWORK_NAME = "network#networkDisplayName";
-
- /**
- * Network mac address
- */
- public static final String CHANNEL_NETWORK_MAC = "network#mac";
-
- /**
- * Name of the channel group type for process information
- */
- public static final String CHANNEL_GROUP_TYPE_CURRENT_PROCESS = "currentProcessGroup";
-
- /**
- * Name of the channel group for process information
- */
- public static final String CHANNEL_GROUP_CURRENT_PROCESS = "currentProcess";
-
- /**
- * CPU load used from a process
- */
-
- public static final String CHANNEL_CURRENT_PROCESS_LOAD = "currentProcess#load";
-
- /**
- * Size of memory used from a process in MB
- */
- public static final String CHANNEL_CURRENT_PROCESS_MEMORY = "currentProcess#used";
-
- /**
- * Name of the process
- */
- public static final String CHANNEL_CURRENT_PROCESS_NAME = "currentProcess#name";
-
- /**
- * Number of threads, used form the process
- */
- public static final String CHANNEL_CURRENT_PROCESS_THREADS = "currentProcess#threads";
-
- /**
- * The full path of the process
- */
- public static final String CHANNEL_CURRENT_PROCESS_PATH = "currentProcess#path";
-
- /**
- * Name of the channel group type for process information
- */
- public static final String CHANNEL_GROUP_TYPE_PROCESS = "processGroup";
-
- /**
- * Name of the channel group for process information
- */
- public static final String CHANNEL_GROUP_PROCESS = "process";
-
- /**
- * CPU load used from a process
- */
-
- public static final String CHANNEL_PROCESS_LOAD = "process#load";
-
- /**
- * Size of memory used from a process in MB
- */
- public static final String CHANNEL_PROCESS_MEMORY = "process#used";
-
- /**
- * Name of the process
- */
- public static final String CHANNEL_PROCESS_NAME = "process#name";
-
- /**
- * Number of threads, used form the process
- */
- public static final String CHANNEL_PROCESS_THREADS = "process#threads";
-
- /**
- * The full path of the process
- */
- public static final String CHANNEL_PROCESS_PATH = "process#path";
-
- // Thing configuraion
- /**
- * Name of the configuration parameter of the thing that defines refresh time for High priority channels
- */
- public static final String HIGH_PRIORITY_REFRESH_TIME = "interval_high";
-
- /**
- * Name of the configuration parameter of the thing that defines refresh time for Medium priority channels
- */
- public static final String MEDIUM_PRIORITY_REFRESH_TIME = "interval_medium";
-
- // Channel configuration
-
- /**
- * Name of the channel configuration parameter priority
- */
- public static final String PRIOIRITY_PARAM = "priority";
-
- /**
- * Name of the channel configuration parameter pid
- *
- */
- public static final String PID_PARAM = "pid";
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal;
-
-import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.systeminfo.internal.handler.SysteminfoHandler;
-import org.openhab.binding.systeminfo.internal.model.SysteminfoInterface;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.binding.BaseThingHandlerFactory;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerFactory;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-/**
- * The {@link SysteminfoHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Lyubomir Papazov - Pass systeminfo service to the SysteminfoHandler constructor
- * @author Wouter Born - Add null annotations
- * @author Mark Herwege - Add dynamic creation of extra channels
- */
-@NonNullByDefault
-@Component(service = ThingHandlerFactory.class, configurationPid = "binding.systeminfo")
-public class SysteminfoHandlerFactory extends BaseThingHandlerFactory {
- private @NonNullByDefault({}) SysteminfoInterface systeminfo;
- private @NonNullByDefault({}) SysteminfoThingTypeProvider thingTypeProvider;
-
- @Override
- public boolean supportsThingType(ThingTypeUID thingTypeUID) {
- return BINDING_ID.equals(thingTypeUID.getBindingId())
- && thingTypeUID.getId().startsWith(THING_TYPE_COMPUTER_ID);
- }
-
- @Override
- protected @Nullable ThingHandler createHandler(Thing thing) {
- ThingTypeUID thingTypeUID = thing.getThingTypeUID();
- if (supportsThingType(thingTypeUID)) {
- String extString = "-" + thing.getUID().getId();
- ThingTypeUID extThingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID + extString);
- if (thingTypeProvider.getThingType(extThingTypeUID, null) == null) {
- thingTypeProvider.createThingType(extThingTypeUID);
- thingTypeProvider.storeChannelsConfig(thing); // Save the current channels configs, will be restored
- // after thing type change.
- }
- return new SysteminfoHandler(thing, thingTypeProvider, systeminfo);
- }
- return null;
- }
-
- @Reference
- public void bindSystemInfo(SysteminfoInterface systeminfo) {
- this.systeminfo = systeminfo;
- }
-
- public void unbindSystemInfo(SysteminfoInterface systeminfo) {
- this.systeminfo = null;
- }
-
- @Reference
- public void setSysteminfoThingTypeProvider(SysteminfoThingTypeProvider thingTypeProvider) {
- this.thingTypeProvider = thingTypeProvider;
- }
-
- public void unsetSysteminfoThingTypeProvider(SysteminfoThingTypeProvider thingTypeProvider) {
- this.thingTypeProvider = null;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal;
-
-import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.*;
-
-import java.net.URI;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.storage.StorageService;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider;
-import org.openhab.core.thing.binding.ThingTypeProvider;
-import org.openhab.core.thing.binding.builder.ChannelBuilder;
-import org.openhab.core.thing.type.ChannelGroupDefinition;
-import org.openhab.core.thing.type.ChannelGroupType;
-import org.openhab.core.thing.type.ChannelGroupTypeRegistry;
-import org.openhab.core.thing.type.ChannelGroupTypeUID;
-import org.openhab.core.thing.type.ChannelType;
-import org.openhab.core.thing.type.ChannelTypeRegistry;
-import org.openhab.core.thing.type.ChannelTypeUID;
-import org.openhab.core.thing.type.ThingType;
-import org.openhab.core.thing.type.ThingTypeBuilder;
-import org.openhab.core.thing.type.ThingTypeRegistry;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended channels can be auto discovered and added to newly created groups in the
- * {@link org.openhab.binding.systeminfo.internal.handler.SysteminfoHandler}. The
- * thing needs to be updated to add the groups. The `SysteminfoThingTypeProvider` OSGi service gives access to the
- * `ThingTypeRegistry` and serves the updated `ThingType`.
- *
- * @author Mark Herwege - Initial contribution
- *
- */
-@NonNullByDefault
-@Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
-public class SysteminfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
- private final Logger logger = LoggerFactory.getLogger(SysteminfoThingTypeProvider.class);
-
- private final ThingTypeRegistry thingTypeRegistry;
- private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
- private final ChannelTypeRegistry channelTypeRegistry;
-
- private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
-
- @Activate
- public SysteminfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
- @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
- @Reference ChannelTypeRegistry channelTypeRegistry, @Reference StorageService storageService) {
- super(storageService);
- this.thingTypeRegistry = thingTypeRegistry;
- this.channelGroupTypeRegistry = channelGroupTypeRegistry;
- this.channelTypeRegistry = channelTypeRegistry;
- }
-
- /**
- * Create thing type with the provided typeUID and add it to the thing type registry.
- *
- * @param typeUID
- * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
- */
- public boolean createThingType(ThingTypeUID typeUID) {
- logger.trace("Creating thing type {}", typeUID);
- return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
- }
-
- /**
- * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
- *
- * @param typeUID
- * @param groupDefs
- * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
- */
- public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
- ThingType baseType = thingTypeRegistry.getThingType(typeUID);
- if (baseType == null) {
- baseType = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
- if (baseType == null) {
- logger.warn("Could not find base thing type in registry.");
- return false;
- }
- }
- ThingTypeBuilder builder = createThingTypeBuilder(typeUID, baseType.getUID());
- if (builder != null) {
- logger.trace("Adding channel group definitions to thing type");
- ThingType type = builder.withChannelGroupDefinitions(groupDefs).build();
-
- putThingType(type);
- return true;
- } else {
- logger.debug("Error adding channel groups");
- return false;
- }
- }
-
- /**
- * Return a {@link ThingTypeBuilder} that can create an exact copy of the `ThingType` with `baseTypeUID`.
- * Further build steps can be performed on the returned object before recreating the `ThingType` from the builder.
- *
- * @param newTypeUID
- * @param baseTypeUID
- * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
- */
- private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
- ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
-
- if (type == null) {
- return null;
- }
-
- ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeUID, type.getLabel())
- .withChannelGroupDefinitions(type.getChannelGroupDefinitions())
- .withChannelDefinitions(type.getChannelDefinitions())
- .withExtensibleChannelTypeIds(type.getExtensibleChannelTypeIds())
- .withSupportedBridgeTypeUIDs(type.getSupportedBridgeTypeUIDs()).withProperties(type.getProperties())
- .isListed(false);
-
- String representationProperty = type.getRepresentationProperty();
- if (representationProperty != null) {
- result = result.withRepresentationProperty(representationProperty);
- }
- URI configDescriptionURI = type.getConfigDescriptionURI();
- if (configDescriptionURI != null) {
- result = result.withConfigDescriptionURI(configDescriptionURI);
- }
- String category = type.getCategory();
- if (category != null) {
- result = result.withCategory(category);
- }
- String description = type.getDescription();
- if (description != null) {
- result = result.withDescription(description);
- }
-
- return result;
- }
-
- /**
- * Return List of {@link ChannelGroupDefinition} for `ThingType` with `typeUID`. If the `ThingType` does not exist
- * in the thingTypeRegistry yet, retrieve list of `ChannelGroupDefinition` for base type systeminfo:computer.
- *
- * @param typeUID UID for ThingType
- * @return list of channel group definitions, empty list if no channel group definitions
- */
- public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
- ThingType type = thingTypeRegistry.getThingType(typeUID);
- if (type == null) {
- type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
- }
- if (type != null) {
- return type.getChannelGroupDefinitions();
- } else {
- logger.debug("Cannot retrieve channel group definitions, no base thing type found");
- return Collections.emptyList();
- }
- }
-
- /**
- * Create a new channel group definition with index appended to id and label.
- *
- * @param channelGroupID id of channel group without index
- * @param channelGroupTypeID id ChannelGroupType for new channel group definition
- * @param i index
- * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
- */
- public @Nullable ChannelGroupDefinition createChannelGroupDefinitionWithIndex(String channelGroupID,
- String channelGroupTypeID, int i) {
- ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(BINDING_ID, channelGroupTypeID);
- ChannelGroupType channelGroupType = channelGroupTypeRegistry.getChannelGroupType(channelGroupTypeUID);
- if (channelGroupType == null) {
- logger.debug("Cannot create channel group definition, group type {} invalid", channelGroupTypeID);
- return null;
- }
- String index = String.valueOf(i);
- return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
- channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
- }
-
- /**
- * Create a new channel with index appended to id and label of an existing channel.
- *
- * @param thing containing the existing channel
- * @param channelID id of channel without index
- * @param i index
- * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
- * provided channel
- */
- public @Nullable Channel createChannelWithIndex(Thing thing, String channelID, int i) {
- Channel baseChannel = thing.getChannel(channelID);
- if (baseChannel == null) {
- logger.debug("Cannot create channel, ID {} invalid", channelID);
- return null;
- }
- ChannelTypeUID channelTypeUID = baseChannel.getChannelTypeUID();
- ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
- if (channelType == null) {
- logger.debug("Cannot create channel, type {} invalid",
- channelTypeUID != null ? channelTypeUID.getId() : "null");
- return null;
- }
- ThingUID thingUID = thing.getUID();
- String index = String.valueOf(i);
- ChannelUID channelUID = new ChannelUID(thingUID, channelID + index);
- ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
- .withConfiguration(baseChannel.getConfiguration());
- builder.withLabel(channelType.getLabel() + " " + index);
- builder.withDefaultTags(channelType.getTags());
- String description = channelType.getDescription();
- if (description != null) {
- builder.withDescription(description);
- }
- String itemType = channelType.getItemType();
- if (itemType != null) {
- builder.withAcceptedItemType(itemType);
- }
- return builder.build();
- }
-
- /**
- * Store the channel configurations for a thing, to be able to restore them later when the thing handler for the
- * same thing gets recreated with a new thing type. This is necessary because the
- * {@link org.openhab.core.thing.binding.BaseThingHandler#changeThingType()} method reverts channel configurations
- * to their defaults.
- *
- * @param thing
- */
- public void storeChannelsConfig(Thing thing) {
- Map<String, Configuration> channelsConfig = thing.getChannels().stream()
- .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
- thingChannelsConfig.put(thing.getUID(), channelsConfig);
- }
-
- /**
- * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
- * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
- * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
- * also {@link #storeChannelsConfig(Thing)}.
- *
- * @param UID
- * @return Map of ChannelId and Configuration for the channel
- */
- public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
- Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
- return configs != null ? configs : Collections.emptyMap();
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal.discovery;
+
+import static org.openhab.binding.systeminfo.internal.SystemInfoBindingConstants.THING_TYPE_COMPUTER;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Discovery service implementation for the SystemInfo binding. It creates {@link DiscoveryResult} with
+ * {@link #DEFAULT_THING_LABEL}. The discovered Thing will have id - the hostname or {@link #DEFAULT_THING_ID}'
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Wouter Born - Add null annotations
+ */
+@NonNullByDefault
+@Component(service = DiscoveryService.class, configurationPid = "discovery.systeminfo")
+public class SystemInfoDiscoveryService extends AbstractDiscoveryService {
+ public static final String DEFAULT_THING_ID = "unknown";
+ public static final String DEFAULT_THING_LABEL = "Local computer";
+
+ private final Logger logger = LoggerFactory.getLogger(SystemInfoDiscoveryService.class);
+
+ private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_COMPUTER);
+
+ private static final int DISCOVERY_TIME_SECONDS = 30;
+ private static final String THING_UID_VALID_CHARS = "A-Za-z0-9_-";
+ private static final String HOST_NAME_SEPERATOR = "_";
+
+ public SystemInfoDiscoveryService() {
+ super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIME_SECONDS);
+ }
+
+ @Override
+ protected void startScan() {
+ logger.debug("Starting system information discovery !");
+ String hostname;
+
+ try {
+ hostname = getHostName();
+ if (hostname.isEmpty()) {
+ throw new UnknownHostException();
+ }
+ if (!hostname.matches("[" + THING_UID_VALID_CHARS + "]*")) {
+ hostname = hostname.replaceAll("[^" + THING_UID_VALID_CHARS + "]", HOST_NAME_SEPERATOR);
+ }
+ } catch (UnknownHostException ex) {
+ hostname = DEFAULT_THING_ID;
+ logger.info("Hostname can not be resolved. Computer name will be set to the default one: {}",
+ DEFAULT_THING_ID);
+ }
+
+ ThingUID computer = new ThingUID(THING_TYPE_COMPUTER, hostname);
+ thingDiscovered(DiscoveryResultBuilder.create(computer).withLabel(DEFAULT_THING_LABEL).build());
+ }
+
+ protected String getHostName() throws UnknownHostException {
+ InetAddress addr = InetAddress.getLocalHost();
+ return addr.getHostName();
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal.discovery;
-
-import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.THING_TYPE_COMPUTER;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants;
-import org.openhab.core.config.discovery.AbstractDiscoveryService;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.osgi.service.component.annotations.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Discovery service implementation for the Systeminfo binding. It creates {@link DiscoveryResult} with
- * {@link #DEFAULT_THING_LABEL}. The discovered Thing will have id - the hostname or {@link #DEFAULT_THING_ID}'
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Wouter Born - Add null annotations
- */
-@NonNullByDefault
-@Component(service = DiscoveryService.class, configurationPid = "discovery.systeminfo")
-public class SysteminfoDiscoveryService extends AbstractDiscoveryService {
- public static final String DEFAULT_THING_ID = "unknown";
- public static final String DEFAULT_THING_LABEL = "Local computer";
-
- private final Logger logger = LoggerFactory.getLogger(SysteminfoDiscoveryService.class);
-
- private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_COMPUTER);
-
- private static final int DISCOVERY_TIME_SECONDS = 30;
- private static final String THING_UID_VALID_CHARS = "A-Za-z0-9_-";
- private static final String HOST_NAME_SEPERATOR = "_";
-
- public SysteminfoDiscoveryService() {
- super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIME_SECONDS);
- }
-
- @Override
- protected void startScan() {
- logger.debug("Starting system information discovery !");
- String hostname;
-
- try {
- hostname = getHostName();
- if (hostname.isEmpty()) {
- throw new UnknownHostException();
- }
- if (!hostname.matches("[" + THING_UID_VALID_CHARS + "]*")) {
- hostname = hostname.replaceAll("[^" + THING_UID_VALID_CHARS + "]", HOST_NAME_SEPERATOR);
- }
- } catch (UnknownHostException ex) {
- hostname = DEFAULT_THING_ID;
- logger.info("Hostname can not be resolved. Computer name will be set to the default one: {}",
- DEFAULT_THING_ID);
- }
-
- ThingTypeUID computerType = SysteminfoBindingConstants.THING_TYPE_COMPUTER;
- ThingUID computer = new ThingUID(computerType, hostname);
- thingDiscovered(DiscoveryResultBuilder.create(computer).withLabel(DEFAULT_THING_LABEL).build());
- }
-
- protected String getHostName() throws UnknownHostException {
- InetAddress addr = InetAddress.getLocalHost();
- return addr.getHostName();
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal.handler;
+
+import static org.openhab.binding.systeminfo.internal.SystemInfoBindingConstants.*;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.systeminfo.internal.SystemInfoThingTypeProvider;
+import org.openhab.binding.systeminfo.internal.model.DeviceNotFoundException;
+import org.openhab.binding.systeminfo.internal.model.SystemInfoInterface;
+import org.openhab.core.cache.ExpiringCache;
+import org.openhab.core.cache.ExpiringCacheMap;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.type.ChannelGroupDefinition;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SystemInfoHandler} is responsible for providing real time information about the system
+ * (CPU, Memory, Storage, Display and others).
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Lyubomir Papzov - Separate the creation of the systeminfo object and its initialization
+ * @author Wouter Born - Add null annotations
+ * @author Mark Herwege - Add dynamic creation of extra channels
+ * @author Mark Herwege - Processor frequency channels
+ */
+@NonNullByDefault
+public class SystemInfoHandler extends BaseThingHandler {
+ /**
+ * Refresh interval for {@link #highPriorityChannels} in seconds.
+ */
+ private @NonNullByDefault({}) BigDecimal refreshIntervalHighPriority;
+
+ /**
+ * Refresh interval for {@link #mediumPriorityChannels} in seconds.
+ */
+ private @NonNullByDefault({}) BigDecimal refreshIntervalMediumPriority;
+
+ /**
+ * Channels with priority configuration parameter set to High. They usually need frequent update of the state like
+ * CPU load, or information about the free and used memory.
+ * They are updated periodically at {@link #refreshIntervalHighPriority}.
+ */
+ private final Set<ChannelUID> highPriorityChannels = new HashSet<>();
+
+ /**
+ * Channels with priority configuration parameter set to Medium. These channels usually need update of the
+ * state not so oft like battery capacity, storage used and etc.
+ * They are updated periodically at {@link #refreshIntervalMediumPriority}.
+ */
+ private final Set<ChannelUID> mediumPriorityChannels = new HashSet<>();
+
+ /**
+ * Channels with priority configuration parameter set to Low. They represent static information or information
+ * that is updated rare- e.g. CPU name, storage name and etc.
+ * They are updated only at {@link #initialize()}.
+ */
+ private final Set<ChannelUID> lowPriorityChannels = new HashSet<>();
+
+ /**
+ * Wait time for the creation of Item-Channel links in seconds. This delay is needed, because the Item-Channel
+ * links have to be created before the thing state is updated, otherwise item state will not be updated.
+ */
+ public static final int WAIT_TIME_CHANNEL_ITEM_LINK_INIT = 1;
+
+ /**
+ * String used to extend thingUID and channelGroupTypeUID for thing definition with added dynamic channels and
+ * extended channels. It is set in the constructor and unique to the thing.
+ */
+ public final String idExtString;
+
+ public final SystemInfoThingTypeProvider thingTypeProvider;
+
+ private SystemInfoInterface systeminfo;
+
+ private @Nullable ScheduledFuture<?> highPriorityTasks;
+ private @Nullable ScheduledFuture<?> mediumPriorityTasks;
+
+ /**
+ * Caches for cpu process load and process load for a given pid. Using this cache limits the process load refresh
+ * interval to the minimum interval. Too frequent refreshes leads to inaccurate results. This could happen when the
+ * same process is tracked as current process and as a channel with pid parameter, or when the task interval is set
+ * too low.
+ */
+ private static final int MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS = 2000;
+ private ExpiringCache<PercentType> cpuLoadCache = new ExpiringCache<>(MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS,
+ () -> getSystemCpuLoad());
+ private ExpiringCacheMap<Integer, @Nullable DecimalType> processLoadCache = new ExpiringCacheMap<>(
+ MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS);
+
+ private final Logger logger = LoggerFactory.getLogger(SystemInfoHandler.class);
+
+ public SystemInfoHandler(Thing thing, SystemInfoThingTypeProvider thingTypeProvider,
+ SystemInfoInterface systeminfo) {
+ super(thing);
+ this.thingTypeProvider = thingTypeProvider;
+ this.systeminfo = systeminfo;
+
+ idExtString = "-" + thing.getUID().getId();
+ }
+
+ @Override
+ public void initialize() {
+ logger.trace("Initializing thing {} with thing type {}", thing.getUID().getId(),
+ thing.getThingTypeUID().getId());
+ restoreChannelsConfig(); // After a thing type change, previous channel configs will have been stored, and will
+ // be restored here.
+ if (instantiateSystemInfoLibrary() && isConfigurationValid() && updateProperties()) {
+ if (!addDynamicChannels()) { // If there are new channel groups, the thing will get recreated with a new
+ // thing type and this handler will be disposed. Therefore do not do anything
+ // further here.
+ groupChannelsByPriority();
+ scheduleUpdates();
+ updateStatus(ThingStatus.ONLINE);
+ }
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "@text/offline.cannot-initialize");
+ }
+ }
+
+ @Override
+ public void handleRemoval() {
+ thingTypeProvider.removeThingType(thing.getThingTypeUID());
+ super.handleRemoval();
+ }
+
+ private boolean instantiateSystemInfoLibrary() {
+ try {
+ systeminfo.initializeSystemInfo();
+ logger.debug("SystemInfo implementation is instantiated!");
+ return true;
+ } catch (Exception e) {
+ logger.warn("Cannot instantiate SystemInfo object!", e);
+ return false;
+ }
+ }
+
+ private boolean isConfigurationValid() {
+ logger.debug("Start reading Thing configuration.");
+ try {
+ refreshIntervalMediumPriority = (BigDecimal) this.thing.getConfiguration()
+ .get(MEDIUM_PRIORITY_REFRESH_TIME);
+ refreshIntervalHighPriority = (BigDecimal) this.thing.getConfiguration().get(HIGH_PRIORITY_REFRESH_TIME);
+
+ if (refreshIntervalHighPriority.intValue() <= 0 || refreshIntervalMediumPriority.intValue() <= 0) {
+ throw new IllegalArgumentException("Refresh time must be positive number!");
+ }
+ logger.debug("Refresh time for medium priority channels set to {} s", refreshIntervalMediumPriority);
+ logger.debug("Refresh time for high priority channels set to {} s", refreshIntervalHighPriority);
+ return true;
+ } catch (IllegalArgumentException e) {
+ logger.warn("Refresh time value is invalid! Please change the thing configuration!");
+ return false;
+ } catch (ClassCastException e) {
+ logger.debug("Channel configuration cannot be read!");
+ return false;
+ }
+ }
+
+ private boolean updateProperties() {
+ Map<String, String> properties = editProperties();
+ try {
+ properties.put(PROPERTY_CPU_LOGICAL_CORES, systeminfo.getCpuLogicalCores().toString());
+ properties.put(PROPERTY_CPU_PHYSICAL_CORES, systeminfo.getCpuPhysicalCores().toString());
+ properties.put(PROPERTY_OS_FAMILY, systeminfo.getOsFamily().toString());
+ properties.put(PROPERTY_OS_MANUFACTURER, systeminfo.getOsManufacturer().toString());
+ properties.put(PROPERTY_OS_VERSION, systeminfo.getOsVersion().toString());
+ updateProperties(properties);
+ logger.debug("Properties updated!");
+ return true;
+ } catch (Exception e) {
+ logger.debug("Cannot get system properties! Please try to restart the binding.", e);
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve info on available storages, drives, displays, batteries, network interfaces and fans in the system. If
+ * there is more than 1, create additional channel groups and channels representing each of the entities with an
+ * index added to the channel groups and channels. The base channel groups and channels will remain without index
+ * and are equal to the channel groups and channels with index 0. If there is only one entity in a group, do not add
+ * a channels group and channels with index 0.
+ * <p>
+ * If channel groups are added, the thing type will change to systeminfo:computer-Ext, with Ext equal to the thing
+ * id. A new handler will be created and initialization restarted. Therefore further initialization of the current
+ * handler can be aborted if the method returns true.
+ *
+ * @return true if channel groups where added
+ */
+ private boolean addDynamicChannels() {
+ ThingUID thingUID = thing.getUID();
+
+ List<ChannelGroupDefinition> newChannelGroups = new ArrayList<>();
+ newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_STORAGE, CHANNEL_GROUP_TYPE_STORAGE,
+ systeminfo.getFileOSStoreCount()));
+ newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_DRIVE, CHANNEL_GROUP_TYPE_DRIVE,
+ systeminfo.getDriveCount()));
+ newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_DISPLAY, CHANNEL_GROUP_TYPE_DISPLAY,
+ systeminfo.getDisplayCount()));
+ newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_BATTERY, CHANNEL_GROUP_TYPE_BATTERY,
+ systeminfo.getPowerSourceCount()));
+ newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_NETWORK, CHANNEL_GROUP_TYPE_NETWORK,
+ systeminfo.getNetworkIFCount()));
+ if (!newChannelGroups.isEmpty()) {
+ logger.debug("Creating additional channel groups");
+ newChannelGroups.addAll(0, thingTypeProvider.getChannelGroupDefinitions(thing.getThingTypeUID()));
+ ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID + idExtString);
+ if (thingTypeProvider.updateThingType(thingTypeUID, newChannelGroups)) {
+ logger.trace("Channel groups were added, changing the thing type");
+ changeThingType(thingTypeUID, thing.getConfiguration());
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "@text/offline.cannot-initialize");
+ }
+ return true;
+ }
+
+ List<Channel> newChannels = new ArrayList<>();
+ newChannels.addAll(createChannels(thingUID, CHANNEL_SENSORS_FAN_SPEED, systeminfo.getFanCount()));
+ newChannels.addAll(createChannels(thingUID, CHANNEL_CPU_FREQ, systeminfo.getCpuLogicalCores().intValue()));
+ if (!newChannels.isEmpty()) {
+ logger.debug("Creating additional channels");
+ newChannels.addAll(0, thing.getChannels());
+ ThingBuilder thingBuilder = editThing();
+ thingBuilder.withChannels(newChannels);
+ updateThing(thingBuilder.build());
+ }
+
+ return false;
+ }
+
+ private List<ChannelGroupDefinition> createChannelGroups(ThingUID thingUID, String channelGroupID,
+ String channelGroupTypeID, int count) {
+ if (count <= 1) {
+ return Collections.emptyList();
+ }
+
+ List<String> channelGroups = thingTypeProvider.getChannelGroupDefinitions(thing.getThingTypeUID()).stream()
+ .map(ChannelGroupDefinition::getId).collect(Collectors.toList());
+
+ List<ChannelGroupDefinition> newChannelGroups = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ String index = String.valueOf(i);
+ ChannelGroupDefinition channelGroupDef = thingTypeProvider
+ .createChannelGroupDefinitionWithIndex(channelGroupID, channelGroupTypeID, i);
+ if (!(channelGroupDef == null || channelGroups.contains(channelGroupID + index))) {
+ logger.trace("Adding channel group {}", channelGroupID + index);
+ newChannelGroups.add(channelGroupDef);
+ }
+ }
+ return newChannelGroups;
+ }
+
+ private List<Channel> createChannels(ThingUID thingUID, String channelID, int count) {
+ if (count <= 1) {
+ return Collections.emptyList();
+ }
+
+ List<Channel> newChannels = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ Channel channel = thingTypeProvider.createChannelWithIndex(thing, channelID, i);
+ if (channel != null && thing.getChannel(channel.getUID()) == null) {
+ logger.trace("Creating channel {}", channel.getUID().getId());
+ newChannels.add(channel);
+ }
+ }
+ return newChannels;
+ }
+
+ private void storeChannelsConfig() {
+ logger.trace("Storing channel configurations");
+ thingTypeProvider.storeChannelsConfig(thing);
+ }
+
+ private void restoreChannelsConfig() {
+ logger.trace("Restoring channel configurations");
+ Map<String, Configuration> channelsConfig = thingTypeProvider.restoreChannelsConfig(thing.getUID());
+ for (String channelId : channelsConfig.keySet()) {
+ Channel channel = thing.getChannel(channelId);
+ Configuration config = channelsConfig.get(channelId);
+ if (channel != null && config != null) {
+ Configuration currentConfig = channel.getConfiguration();
+ for (String param : config.keySet()) {
+ if (isConfigurationKeyChanged(currentConfig, config, param)) {
+ handleChannelConfigurationChange(channel, config, param);
+ }
+ }
+ }
+ }
+ }
+
+ private void groupChannelsByPriority() {
+ logger.trace("Grouping channels by priority");
+ List<Channel> channels = this.thing.getChannels();
+
+ for (Channel channel : channels) {
+ Configuration properties = channel.getConfiguration();
+ String priority = (String) properties.get(PRIOIRITY_PARAM);
+ if (priority == null) {
+ logger.debug("Channel with UID {} will not be updated. The channel has no priority set!",
+ channel.getUID());
+ break;
+ }
+ switch (priority) {
+ case "High":
+ highPriorityChannels.add(channel.getUID());
+ break;
+ case "Medium":
+ mediumPriorityChannels.add(channel.getUID());
+ break;
+ case "Low":
+ lowPriorityChannels.add(channel.getUID());
+ break;
+ default:
+ logger.debug("Invalid priority configuration parameter. Channel will not be updated!");
+ }
+ }
+ }
+
+ private void changeChannelPriority(ChannelUID channelUID, String priority) {
+ switch (priority) {
+ case "High":
+ mediumPriorityChannels.remove(channelUID);
+ lowPriorityChannels.remove(channelUID);
+ highPriorityChannels.add(channelUID);
+ break;
+ case "Medium":
+ lowPriorityChannels.remove(channelUID);
+ highPriorityChannels.remove(channelUID);
+ mediumPriorityChannels.add(channelUID);
+ break;
+ case "Low":
+ highPriorityChannels.remove(channelUID);
+ mediumPriorityChannels.remove(channelUID);
+ lowPriorityChannels.add(channelUID);
+ break;
+ default:
+ logger.debug("Invalid priority configuration parameter. Channel will not be updated!");
+ }
+ }
+
+ private void scheduleUpdates() {
+ logger.debug("Schedule high priority tasks at fixed rate {} s", refreshIntervalHighPriority);
+ highPriorityTasks = scheduler.scheduleWithFixedDelay(() -> {
+ publishData(highPriorityChannels);
+ }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, refreshIntervalHighPriority.intValue(), TimeUnit.SECONDS);
+
+ logger.debug("Schedule medium priority tasks at fixed rate {} s", refreshIntervalMediumPriority);
+ mediumPriorityTasks = scheduler.scheduleWithFixedDelay(() -> {
+ publishData(mediumPriorityChannels);
+ }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, refreshIntervalMediumPriority.intValue(), TimeUnit.SECONDS);
+
+ logger.debug("Schedule one time update for low priority tasks");
+ scheduler.schedule(() -> {
+ publishData(lowPriorityChannels);
+ }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, TimeUnit.SECONDS);
+ }
+
+ private void publishData(Set<ChannelUID> channels) {
+ // if handler disposed while waiting for the links, don't update the channel states
+ if (!ThingStatus.ONLINE.equals(thing.getStatus())) {
+ return;
+ }
+ for (ChannelUID channeUID : channels) {
+ if (isLinked(channeUID)) {
+ publishDataForChannel(channeUID);
+ }
+ }
+ }
+
+ private void publishDataForChannel(ChannelUID channelUID) {
+ State state = getInfoForChannel(channelUID);
+ String channelID = channelUID.getId();
+ updateState(channelID, state);
+ }
+
+ public Set<ChannelUID> getHighPriorityChannels() {
+ return highPriorityChannels;
+ }
+
+ public Set<ChannelUID> getMediumPriorityChannels() {
+ return mediumPriorityChannels;
+ }
+
+ public Set<ChannelUID> getLowPriorityChannels() {
+ return lowPriorityChannels;
+ }
+
+ /**
+ * This method gets the information for specific channel through the {@link SystemInfoInterface}. It uses the
+ * channel ID to call the correct method from the {@link SystemInfoInterface} with deviceIndex parameter (in case of
+ * multiple devices, for reference see {@link SystemInfoHandler#getDeviceIndex(ChannelUID)}})
+ *
+ * @param channelUID the UID of the channel
+ * @return State object or null, if there is no information for the device with this index
+ */
+ private State getInfoForChannel(ChannelUID channelUID) {
+ State state = null;
+
+ String channelID = channelUID.getId();
+ int deviceIndex = getDeviceIndex(channelUID);
+
+ logger.trace("Getting state for channel {} with device index {}", channelID, deviceIndex);
+
+ // The channelGroup or channel may contain deviceIndex. It must be deleted from the channelID, because otherwise
+ // the switch will not find the correct method below.
+ // All digits are deleted from the ID, except for CpuLoad channels.
+ if (!(CHANNEL_CPU_LOAD_1.equals(channelID) || CHANNEL_CPU_LOAD_5.equals(channelID)
+ || CHANNEL_CPU_LOAD_15.equals(channelID))) {
+ channelID = channelID.replaceAll("\\d+", "");
+ }
+
+ try {
+ switch (channelID) {
+ case CHANNEL_MEMORY_HEAP_AVAILABLE:
+ state = new QuantityType<>(Runtime.getRuntime().freeMemory(), Units.BYTE);
+ break;
+ case CHANNEL_MEMORY_USED_HEAP_PERCENT:
+ state = new QuantityType<>((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
+ * 100 / Runtime.getRuntime().maxMemory(), Units.PERCENT);
+ break;
+ case CHANNEL_DISPLAY_INFORMATION:
+ state = systeminfo.getDisplayInformation(deviceIndex);
+ break;
+ case CHANNEL_BATTERY_NAME:
+ state = systeminfo.getBatteryName(deviceIndex);
+ break;
+ case CHANNEL_BATTERY_REMAINING_CAPACITY:
+ state = new QuantityType<>(systeminfo.getBatteryRemainingCapacity(deviceIndex), Units.PERCENT);
+ break;
+ case CHANNEL_BATTERY_REMAINING_TIME:
+ state = systeminfo.getBatteryRemainingTime(deviceIndex);
+ break;
+ case CHANNEL_SENSORS_CPU_TEMPERATURE:
+ state = systeminfo.getSensorsCpuTemperature();
+ break;
+ case CHANNEL_SENOSRS_CPU_VOLTAGE:
+ state = systeminfo.getSensorsCpuVoltage();
+ break;
+ case CHANNEL_SENSORS_FAN_SPEED:
+ state = systeminfo.getSensorsFanSpeed(deviceIndex);
+ break;
+ case CHANNEL_CPU_MAXFREQ:
+ state = systeminfo.getCpuMaxFreq();
+ break;
+ case CHANNEL_CPU_FREQ:
+ state = systeminfo.getCpuFreq(deviceIndex);
+ break;
+ case CHANNEL_CPU_LOAD:
+ PercentType cpuLoad = cpuLoadCache.getValue();
+ state = (cpuLoad != null) ? new QuantityType<>(cpuLoad, Units.PERCENT) : null;
+ break;
+ case CHANNEL_CPU_LOAD_1:
+ state = systeminfo.getCpuLoad1();
+ break;
+ case CHANNEL_CPU_LOAD_5:
+ state = systeminfo.getCpuLoad5();
+ break;
+ case CHANNEL_CPU_LOAD_15:
+ state = systeminfo.getCpuLoad15();
+ break;
+ case CHANNEL_CPU_UPTIME:
+ state = systeminfo.getCpuUptime();
+ break;
+ case CHANNEL_CPU_THREADS:
+ state = systeminfo.getCpuThreads();
+ break;
+ case CHANNEL_CPU_DESCRIPTION:
+ state = systeminfo.getCpuDescription();
+ break;
+ case CHANNEL_CPU_NAME:
+ state = systeminfo.getCpuName();
+ break;
+ case CHANNEL_MEMORY_AVAILABLE:
+ state = systeminfo.getMemoryAvailable();
+ break;
+ case CHANNEL_MEMORY_USED:
+ state = systeminfo.getMemoryUsed();
+ break;
+ case CHANNEL_MEMORY_TOTAL:
+ state = systeminfo.getMemoryTotal();
+ break;
+ case CHANNEL_MEMORY_AVAILABLE_PERCENT:
+ PercentType memoryAvailablePercent = systeminfo.getMemoryAvailablePercent();
+ state = (memoryAvailablePercent != null) ? new QuantityType<>(memoryAvailablePercent, Units.PERCENT)
+ : null;
+ break;
+ case CHANNEL_MEMORY_USED_PERCENT:
+ PercentType memoryUsedPercent = systeminfo.getMemoryUsedPercent();
+ state = (memoryUsedPercent != null) ? new QuantityType<>(memoryUsedPercent, Units.PERCENT) : null;
+ break;
+ case CHANNEL_SWAP_AVAILABLE:
+ state = systeminfo.getSwapAvailable();
+ break;
+ case CHANNEL_SWAP_USED:
+ state = systeminfo.getSwapUsed();
+ break;
+ case CHANNEL_SWAP_TOTAL:
+ state = systeminfo.getSwapTotal();
+ break;
+ case CHANNEL_SWAP_AVAILABLE_PERCENT:
+ PercentType swapAvailablePercent = systeminfo.getSwapAvailablePercent();
+ state = (swapAvailablePercent != null) ? new QuantityType<>(swapAvailablePercent, Units.PERCENT)
+ : null;
+ break;
+ case CHANNEL_SWAP_USED_PERCENT:
+ PercentType swapUsedPercent = systeminfo.getSwapUsedPercent();
+ state = (swapUsedPercent != null) ? new QuantityType<>(swapUsedPercent, Units.PERCENT) : null;
+ break;
+ case CHANNEL_DRIVE_MODEL:
+ state = systeminfo.getDriveModel(deviceIndex);
+ break;
+ case CHANNEL_DRIVE_SERIAL:
+ state = systeminfo.getDriveSerialNumber(deviceIndex);
+ break;
+ case CHANNEL_DRIVE_NAME:
+ state = systeminfo.getDriveName(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_NAME:
+ state = systeminfo.getStorageName(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_DESCRIPTION:
+ state = systeminfo.getStorageDescription(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_AVAILABLE:
+ state = systeminfo.getStorageAvailable(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_USED:
+ state = systeminfo.getStorageUsed(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_TOTAL:
+ state = systeminfo.getStorageTotal(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_TYPE:
+ state = systeminfo.getStorageType(deviceIndex);
+ break;
+ case CHANNEL_STORAGE_AVAILABLE_PERCENT:
+ PercentType storageAvailablePercent = systeminfo.getStorageAvailablePercent(deviceIndex);
+ state = (storageAvailablePercent != null)
+ ? new QuantityType<>(storageAvailablePercent, Units.PERCENT)
+ : null;
+ break;
+ case CHANNEL_STORAGE_USED_PERCENT:
+ PercentType storageUsedPercent = systeminfo.getStorageUsedPercent(deviceIndex);
+ state = (storageUsedPercent != null) ? new QuantityType<>(storageUsedPercent, Units.PERCENT) : null;
+ break;
+ case CHANNEL_NETWORK_IP:
+ state = systeminfo.getNetworkIp(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_ADAPTER_NAME:
+ state = systeminfo.getNetworkDisplayName(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_NAME:
+ state = systeminfo.getNetworkName(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_MAC:
+ state = systeminfo.getNetworkMac(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_DATA_SENT:
+ state = systeminfo.getNetworkDataSent(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_DATA_RECEIVED:
+ state = systeminfo.getNetworkDataReceived(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_PACKETS_RECEIVED:
+ state = systeminfo.getNetworkPacketsReceived(deviceIndex);
+ break;
+ case CHANNEL_NETWORK_PACKETS_SENT:
+ state = systeminfo.getNetworkPacketsSent(deviceIndex);
+ break;
+ case CHANNEL_PROCESS_LOAD:
+ case CHANNEL_CURRENT_PROCESS_LOAD:
+ DecimalType processLoad = processLoadCache.putIfAbsentAndGet(deviceIndex,
+ () -> getProcessCpuUsage(deviceIndex));
+ state = (processLoad != null) ? new QuantityType<>(processLoad, Units.PERCENT) : null;
+ break;
+ case CHANNEL_PROCESS_MEMORY:
+ case CHANNEL_CURRENT_PROCESS_MEMORY:
+ state = systeminfo.getProcessMemoryUsage(deviceIndex);
+ break;
+ case CHANNEL_PROCESS_NAME:
+ case CHANNEL_CURRENT_PROCESS_NAME:
+ state = systeminfo.getProcessName(deviceIndex);
+ break;
+ case CHANNEL_PROCESS_PATH:
+ case CHANNEL_CURRENT_PROCESS_PATH:
+ state = systeminfo.getProcessPath(deviceIndex);
+ break;
+ case CHANNEL_PROCESS_THREADS:
+ case CHANNEL_CURRENT_PROCESS_THREADS:
+ state = systeminfo.getProcessThreads(deviceIndex);
+ break;
+ default:
+ logger.debug("Channel with unknown ID: {} !", channelID);
+ }
+ } catch (DeviceNotFoundException e) {
+ logger.warn("No information for channel {} with device index: {}", channelID, deviceIndex);
+ } catch (Exception e) {
+ logger.debug("Unexpected error occurred while getting system information!", e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/offline.unexpected-error");
+ }
+ return state != null ? state : UnDefType.UNDEF;
+ }
+
+ private @Nullable PercentType getSystemCpuLoad() {
+ return systeminfo.getSystemCpuLoad();
+ }
+
+ private @Nullable DecimalType getProcessCpuUsage(int pid) {
+ try {
+ return systeminfo.getProcessCpuUsage(pid);
+ } catch (DeviceNotFoundException e) {
+ logger.warn("Process with pid {} does not exist", pid);
+ return null;
+ }
+ }
+
+ /**
+ * The device index is an optional part of the channelID - the last characters of the groupID. It is used to
+ * identify unique device, when more than one devices are available (e.g. local disks with names C:\, D:\, E"\ - the
+ * first will have deviceIndex=0, the second deviceIndex=1 ant etc).
+ * When no device index is specified, default value of 0 (first device in the list) is returned.
+ *
+ * @param channelUID the ID of the channel
+ * @return natural number (number >=0)
+ */
+ private int getDeviceIndex(ChannelUID channelUID) {
+ String channelID = channelUID.getId();
+ String channelGroupID = channelUID.getGroupId();
+ if (channelGroupID == null) {
+ return 0;
+ }
+
+ if (channelGroupID.contains(CHANNEL_GROUP_PROCESS)) {
+ // Only in this case the deviceIndex is part of the channel configuration - PID (Process Identifier)
+ int pid = getPID(channelUID);
+ logger.debug("Channel with UID {} tracks process with PID: {}", channelUID, pid);
+ return pid;
+ }
+
+ if (channelGroupID.contains(CHANNEL_GROUP_CURRENT_PROCESS)) {
+ return systeminfo.getCurrentProcessID();
+ }
+
+ // First try to get device index in group id, delete all non-digits from id
+ if (Character.isDigit(channelGroupID.charAt(channelGroupID.length() - 1))) {
+ String deviceIndexPart = channelGroupID.replaceAll("\\D+", "");
+ return Integer.parseInt(deviceIndexPart);
+ }
+
+ // If not found, try to find it in channel id, delete all non-digits from id
+ if (Character.isDigit(channelID.charAt(channelID.length() - 1))) {
+ String deviceIndexPart = channelID.replaceAll("\\D+", "");
+ return Integer.parseInt(deviceIndexPart);
+ }
+
+ return 0;
+ }
+
+ /**
+ * This method gets the process identifier (PID) for specific process
+ *
+ * @param channelUID channel unique identifier
+ * @return natural number
+ */
+ private int getPID(ChannelUID channelUID) {
+ int pid = 0;
+ try {
+ Channel channel = this.thing.getChannel(channelUID.getId());
+ if (channel != null) {
+ Configuration channelProperties = channel.getConfiguration();
+ BigDecimal pidValue = (BigDecimal) channelProperties.get(PID_PARAM);
+ if (pidValue == null || pidValue.intValue() < 0) {
+ throw new IllegalArgumentException("Invalid value for Process Identifier.");
+ } else {
+ pid = pidValue.intValue();
+ }
+ } else {
+ logger.debug("Channel does not exist! Fall back to default value.");
+ }
+ } catch (ClassCastException e) {
+ logger.debug("Channel configuration cannot be read! Fall back to default value.", e);
+ } catch (IllegalArgumentException e) {
+ logger.debug("PID (Process Identifier) must be positive number. Fall back to default value. ", e);
+ }
+ return pid;
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (thing.getStatus().equals(ThingStatus.ONLINE)) {
+ if (command instanceof RefreshType) {
+ logger.debug("Refresh command received for channel {} !", channelUID);
+ publishDataForChannel(channelUID);
+ } else {
+ logger.debug("Unsupported command {} ! Supported commands: REFRESH", command);
+ }
+ } else {
+ logger.debug("Cannot handle command. Thing is not ONLINE.");
+ }
+ }
+
+ private boolean isConfigurationKeyChanged(Configuration currentConfig, Configuration newConfig, String key) {
+ Object currentValue = currentConfig.get(key);
+ Object newValue = newConfig.get(key);
+
+ if (currentValue == null) {
+ return (newValue != null);
+ }
+
+ return !currentValue.equals(newValue);
+ }
+
+ @Override
+ public synchronized void thingUpdated(Thing thing) {
+ logger.trace("About to update thing");
+ boolean isChannelConfigChanged = false;
+
+ List<Channel> channels = thing.getChannels();
+
+ for (Channel channel : channels) {
+ ChannelUID channelUID = channel.getUID();
+ Configuration newChannelConfig = channel.getConfiguration();
+ Channel oldChannel = this.thing.getChannel(channelUID.getId());
+
+ if (oldChannel == null) {
+ logger.warn("Channel with UID {} cannot be updated, as it cannot be found!", channelUID);
+ continue;
+ }
+ Configuration currentChannelConfig = oldChannel.getConfiguration();
+
+ if (isConfigurationKeyChanged(currentChannelConfig, newChannelConfig, PRIOIRITY_PARAM)) {
+ isChannelConfigChanged = true;
+
+ handleChannelConfigurationChange(oldChannel, newChannelConfig, PRIOIRITY_PARAM);
+
+ String newPriority = (String) newChannelConfig.get(PRIOIRITY_PARAM);
+ changeChannelPriority(channelUID, newPriority);
+ }
+
+ if (isConfigurationKeyChanged(currentChannelConfig, newChannelConfig, PID_PARAM)) {
+ isChannelConfigChanged = true;
+ handleChannelConfigurationChange(oldChannel, newChannelConfig, PID_PARAM);
+ }
+ }
+
+ if (!(isInitialized() && isChannelConfigChanged)) {
+ super.thingUpdated(thing);
+ }
+ }
+
+ private void handleChannelConfigurationChange(Channel channel, Configuration newConfig, String parameter) {
+ Configuration configuration = channel.getConfiguration();
+ Object oldValue = configuration.get(parameter);
+
+ configuration.put(parameter, newConfig.get(parameter));
+
+ Object newValue = newConfig.get(parameter);
+ logger.debug("Channel with UID {} has changed its {} from {} to {}", channel.getUID(), parameter, oldValue,
+ newValue);
+ publishDataForChannel(channel.getUID());
+ }
+
+ @Override
+ protected void changeThingType(ThingTypeUID thingTypeUID, Configuration configuration) {
+ storeChannelsConfig();
+ super.changeThingType(thingTypeUID, configuration);
+ }
+
+ private void stopScheduledUpdates() {
+ ScheduledFuture<?> localHighPriorityTasks = highPriorityTasks;
+ if (localHighPriorityTasks != null) {
+ logger.debug("High prioriy tasks will not be run anymore!");
+ localHighPriorityTasks.cancel(true);
+ }
+
+ ScheduledFuture<?> localMediumPriorityTasks = mediumPriorityTasks;
+ if (localMediumPriorityTasks != null) {
+ logger.debug("Medium prioriy tasks will not be run anymore!");
+ localMediumPriorityTasks.cancel(true);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ stopScheduledUpdates();
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal.handler;
-
-import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.*;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.systeminfo.internal.SysteminfoThingTypeProvider;
-import org.openhab.binding.systeminfo.internal.model.DeviceNotFoundException;
-import org.openhab.binding.systeminfo.internal.model.SysteminfoInterface;
-import org.openhab.core.cache.ExpiringCache;
-import org.openhab.core.cache.ExpiringCacheMap;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.thing.binding.builder.ThingBuilder;
-import org.openhab.core.thing.type.ChannelGroupDefinition;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link SysteminfoHandler} is responsible for providing real time information about the system
- * (CPU, Memory, Storage, Display and others).
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Lyubomir Papzov - Separate the creation of the systeminfo object and its initialization
- * @author Wouter Born - Add null annotations
- * @author Mark Herwege - Add dynamic creation of extra channels
- * @author Mark Herwege - Processor frequency channels
- */
-@NonNullByDefault
-public class SysteminfoHandler extends BaseThingHandler {
- /**
- * Refresh interval for {@link #highPriorityChannels} in seconds.
- */
- private @NonNullByDefault({}) BigDecimal refreshIntervalHighPriority;
-
- /**
- * Refresh interval for {@link #mediumPriorityChannels} in seconds.
- */
- private @NonNullByDefault({}) BigDecimal refreshIntervalMediumPriority;
-
- /**
- * Channels with priority configuration parameter set to High. They usually need frequent update of the state like
- * CPU load, or information about the free and used memory.
- * They are updated periodically at {@link #refreshIntervalHighPriority}.
- */
- private final Set<ChannelUID> highPriorityChannels = new HashSet<>();
-
- /**
- * Channels with priority configuration parameter set to Medium. These channels usually need update of the
- * state not so oft like battery capacity, storage used and etc.
- * They are updated periodically at {@link #refreshIntervalMediumPriority}.
- */
- private final Set<ChannelUID> mediumPriorityChannels = new HashSet<>();
-
- /**
- * Channels with priority configuration parameter set to Low. They represent static information or information
- * that is updated rare- e.g. CPU name, storage name and etc.
- * They are updated only at {@link #initialize()}.
- */
- private final Set<ChannelUID> lowPriorityChannels = new HashSet<>();
-
- /**
- * Wait time for the creation of Item-Channel links in seconds. This delay is needed, because the Item-Channel
- * links have to be created before the thing state is updated, otherwise item state will not be updated.
- */
- public static final int WAIT_TIME_CHANNEL_ITEM_LINK_INIT = 1;
-
- /**
- * String used to extend thingUID and channelGroupTypeUID for thing definition with added dynamic channels and
- * extended channels. It is set in the constructor and unique to the thing.
- */
- public final String idExtString;
-
- public final SysteminfoThingTypeProvider thingTypeProvider;
-
- private SysteminfoInterface systeminfo;
-
- private @Nullable ScheduledFuture<?> highPriorityTasks;
- private @Nullable ScheduledFuture<?> mediumPriorityTasks;
-
- /**
- * Caches for cpu process load and process load for a given pid. Using this cache limits the process load refresh
- * interval to the minimum interval. Too frequent refreshes leads to inaccurate results. This could happen when the
- * same process is tracked as current process and as a channel with pid parameter, or when the task interval is set
- * too low.
- */
- private static final int MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS = 2000;
- private ExpiringCache<PercentType> cpuLoadCache = new ExpiringCache<>(MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS,
- () -> getSystemCpuLoad());
- private ExpiringCacheMap<Integer, @Nullable DecimalType> processLoadCache = new ExpiringCacheMap<>(
- MIN_PROCESS_LOAD_REFRESH_INTERVAL_MS);
-
- private final Logger logger = LoggerFactory.getLogger(SysteminfoHandler.class);
-
- public SysteminfoHandler(Thing thing, SysteminfoThingTypeProvider thingTypeProvider,
- SysteminfoInterface systeminfo) {
- super(thing);
- this.thingTypeProvider = thingTypeProvider;
- this.systeminfo = systeminfo;
-
- idExtString = "-" + thing.getUID().getId();
- }
-
- @Override
- public void initialize() {
- logger.trace("Initializing thing {} with thing type {}", thing.getUID().getId(),
- thing.getThingTypeUID().getId());
- restoreChannelsConfig(); // After a thing type change, previous channel configs will have been stored, and will
- // be restored here.
- if (instantiateSysteminfoLibrary() && isConfigurationValid() && updateProperties()) {
- if (!addDynamicChannels()) { // If there are new channel groups, the thing will get recreated with a new
- // thing type and this handler will be disposed. Therefore do not do anything
- // further here.
- groupChannelsByPriority();
- scheduleUpdates();
- updateStatus(ThingStatus.ONLINE);
- }
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
- "@text/offline.cannot-initialize");
- }
- }
-
- @Override
- public void handleRemoval() {
- thingTypeProvider.removeThingType(thing.getThingTypeUID());
- super.handleRemoval();
- }
-
- private boolean instantiateSysteminfoLibrary() {
- try {
- systeminfo.initializeSysteminfo();
- logger.debug("Systeminfo implementation is instantiated!");
- return true;
- } catch (Exception e) {
- logger.warn("Cannot instantiate Systeminfo object!", e);
- return false;
- }
- }
-
- private boolean isConfigurationValid() {
- logger.debug("Start reading Thing configuration.");
- try {
- refreshIntervalMediumPriority = (BigDecimal) this.thing.getConfiguration()
- .get(MEDIUM_PRIORITY_REFRESH_TIME);
- refreshIntervalHighPriority = (BigDecimal) this.thing.getConfiguration().get(HIGH_PRIORITY_REFRESH_TIME);
-
- if (refreshIntervalHighPriority.intValue() <= 0 || refreshIntervalMediumPriority.intValue() <= 0) {
- throw new IllegalArgumentException("Refresh time must be positive number!");
- }
- logger.debug("Refresh time for medium priority channels set to {} s", refreshIntervalMediumPriority);
- logger.debug("Refresh time for high priority channels set to {} s", refreshIntervalHighPriority);
- return true;
- } catch (IllegalArgumentException e) {
- logger.warn("Refresh time value is invalid! Please change the thing configuration!");
- return false;
- } catch (ClassCastException e) {
- logger.debug("Channel configuration cannot be read!");
- return false;
- }
- }
-
- private boolean updateProperties() {
- Map<String, String> properties = editProperties();
- try {
- properties.put(PROPERTY_CPU_LOGICAL_CORES, systeminfo.getCpuLogicalCores().toString());
- properties.put(PROPERTY_CPU_PHYSICAL_CORES, systeminfo.getCpuPhysicalCores().toString());
- properties.put(PROPERTY_OS_FAMILY, systeminfo.getOsFamily().toString());
- properties.put(PROPERTY_OS_MANUFACTURER, systeminfo.getOsManufacturer().toString());
- properties.put(PROPERTY_OS_VERSION, systeminfo.getOsVersion().toString());
- updateProperties(properties);
- logger.debug("Properties updated!");
- return true;
- } catch (Exception e) {
- logger.debug("Cannot get system properties! Please try to restart the binding.", e);
- return false;
- }
- }
-
- /**
- * Retrieve info on available storages, drives, displays, batteries, network interfaces and fans in the system. If
- * there is more than 1, create additional channel groups and channels representing each of the entities with an
- * index added to the channel groups and channels. The base channel groups and channels will remain without index
- * and are equal to the channel groups and channels with index 0. If there is only one entity in a group, do not add
- * a channels group and channels with index 0.
- * <p>
- * If channel groups are added, the thing type will change to systeminfo:computer-Ext, with Ext equal to the thing
- * id. A new handler will be created and initialization restarted. Therefore further initialization of the current
- * handler can be aborted if the method returns true.
- *
- * @return true if channel groups where added
- */
- private boolean addDynamicChannels() {
- ThingUID thingUID = thing.getUID();
-
- List<ChannelGroupDefinition> newChannelGroups = new ArrayList<>();
- newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_STORAGE, CHANNEL_GROUP_TYPE_STORAGE,
- systeminfo.getFileOSStoreCount()));
- newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_DRIVE, CHANNEL_GROUP_TYPE_DRIVE,
- systeminfo.getDriveCount()));
- newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_DISPLAY, CHANNEL_GROUP_TYPE_DISPLAY,
- systeminfo.getDisplayCount()));
- newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_BATTERY, CHANNEL_GROUP_TYPE_BATTERY,
- systeminfo.getPowerSourceCount()));
- newChannelGroups.addAll(createChannelGroups(thingUID, CHANNEL_GROUP_NETWORK, CHANNEL_GROUP_TYPE_NETWORK,
- systeminfo.getNetworkIFCount()));
- if (!newChannelGroups.isEmpty()) {
- logger.debug("Creating additional channel groups");
- newChannelGroups.addAll(0, thingTypeProvider.getChannelGroupDefinitions(thing.getThingTypeUID()));
- ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_COMPUTER_ID + idExtString);
- if (thingTypeProvider.updateThingType(thingTypeUID, newChannelGroups)) {
- logger.trace("Channel groups were added, changing the thing type");
- changeThingType(thingTypeUID, thing.getConfiguration());
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
- "@text/offline.cannot-initialize");
- }
- return true;
- }
-
- List<Channel> newChannels = new ArrayList<>();
- newChannels.addAll(createChannels(thingUID, CHANNEL_SENSORS_FAN_SPEED, systeminfo.getFanCount()));
- newChannels.addAll(createChannels(thingUID, CHANNEL_CPU_FREQ, systeminfo.getCpuLogicalCores().intValue()));
- if (!newChannels.isEmpty()) {
- logger.debug("Creating additional channels");
- newChannels.addAll(0, thing.getChannels());
- ThingBuilder thingBuilder = editThing();
- thingBuilder.withChannels(newChannels);
- updateThing(thingBuilder.build());
- }
-
- return false;
- }
-
- private List<ChannelGroupDefinition> createChannelGroups(ThingUID thingUID, String channelGroupID,
- String channelGroupTypeID, int count) {
- if (count <= 1) {
- return Collections.emptyList();
- }
-
- List<String> channelGroups = thingTypeProvider.getChannelGroupDefinitions(thing.getThingTypeUID()).stream()
- .map(ChannelGroupDefinition::getId).collect(Collectors.toList());
-
- List<ChannelGroupDefinition> newChannelGroups = new ArrayList<>();
- for (int i = 0; i < count; i++) {
- String index = String.valueOf(i);
- ChannelGroupDefinition channelGroupDef = thingTypeProvider
- .createChannelGroupDefinitionWithIndex(channelGroupID, channelGroupTypeID, i);
- if (!(channelGroupDef == null || channelGroups.contains(channelGroupID + index))) {
- logger.trace("Adding channel group {}", channelGroupID + index);
- newChannelGroups.add(channelGroupDef);
- }
- }
- return newChannelGroups;
- }
-
- private List<Channel> createChannels(ThingUID thingUID, String channelID, int count) {
- if (count <= 1) {
- return Collections.emptyList();
- }
-
- List<Channel> newChannels = new ArrayList<>();
- for (int i = 0; i < count; i++) {
- Channel channel = thingTypeProvider.createChannelWithIndex(thing, channelID, i);
- if (channel != null && thing.getChannel(channel.getUID()) == null) {
- logger.trace("Creating channel {}", channel.getUID().getId());
- newChannels.add(channel);
- }
- }
- return newChannels;
- }
-
- private void storeChannelsConfig() {
- logger.trace("Storing channel configurations");
- thingTypeProvider.storeChannelsConfig(thing);
- }
-
- private void restoreChannelsConfig() {
- logger.trace("Restoring channel configurations");
- Map<String, Configuration> channelsConfig = thingTypeProvider.restoreChannelsConfig(thing.getUID());
- for (String channelId : channelsConfig.keySet()) {
- Channel channel = thing.getChannel(channelId);
- Configuration config = channelsConfig.get(channelId);
- if (channel != null && config != null) {
- Configuration currentConfig = channel.getConfiguration();
- for (String param : config.keySet()) {
- if (isConfigurationKeyChanged(currentConfig, config, param)) {
- handleChannelConfigurationChange(channel, config, param);
- }
- }
- }
- }
- }
-
- private void groupChannelsByPriority() {
- logger.trace("Grouping channels by priority");
- List<Channel> channels = this.thing.getChannels();
-
- for (Channel channel : channels) {
- Configuration properties = channel.getConfiguration();
- String priority = (String) properties.get(PRIOIRITY_PARAM);
- if (priority == null) {
- logger.debug("Channel with UID {} will not be updated. The channel has no priority set!",
- channel.getUID());
- break;
- }
- switch (priority) {
- case "High":
- highPriorityChannels.add(channel.getUID());
- break;
- case "Medium":
- mediumPriorityChannels.add(channel.getUID());
- break;
- case "Low":
- lowPriorityChannels.add(channel.getUID());
- break;
- default:
- logger.debug("Invalid priority configuration parameter. Channel will not be updated!");
- }
- }
- }
-
- private void changeChannelPriority(ChannelUID channelUID, String priority) {
- switch (priority) {
- case "High":
- mediumPriorityChannels.remove(channelUID);
- lowPriorityChannels.remove(channelUID);
- highPriorityChannels.add(channelUID);
- break;
- case "Medium":
- lowPriorityChannels.remove(channelUID);
- highPriorityChannels.remove(channelUID);
- mediumPriorityChannels.add(channelUID);
- break;
- case "Low":
- highPriorityChannels.remove(channelUID);
- mediumPriorityChannels.remove(channelUID);
- lowPriorityChannels.add(channelUID);
- break;
- default:
- logger.debug("Invalid priority configuration parameter. Channel will not be updated!");
- }
- }
-
- private void scheduleUpdates() {
- logger.debug("Schedule high priority tasks at fixed rate {} s", refreshIntervalHighPriority);
- highPriorityTasks = scheduler.scheduleWithFixedDelay(() -> {
- publishData(highPriorityChannels);
- }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, refreshIntervalHighPriority.intValue(), TimeUnit.SECONDS);
-
- logger.debug("Schedule medium priority tasks at fixed rate {} s", refreshIntervalMediumPriority);
- mediumPriorityTasks = scheduler.scheduleWithFixedDelay(() -> {
- publishData(mediumPriorityChannels);
- }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, refreshIntervalMediumPriority.intValue(), TimeUnit.SECONDS);
-
- logger.debug("Schedule one time update for low priority tasks");
- scheduler.schedule(() -> {
- publishData(lowPriorityChannels);
- }, WAIT_TIME_CHANNEL_ITEM_LINK_INIT, TimeUnit.SECONDS);
- }
-
- private void publishData(Set<ChannelUID> channels) {
- // if handler disposed while waiting for the links, don't update the channel states
- if (!ThingStatus.ONLINE.equals(thing.getStatus())) {
- return;
- }
- for (ChannelUID channeUID : channels) {
- if (isLinked(channeUID)) {
- publishDataForChannel(channeUID);
- }
- }
- }
-
- private void publishDataForChannel(ChannelUID channelUID) {
- State state = getInfoForChannel(channelUID);
- String channelID = channelUID.getId();
- updateState(channelID, state);
- }
-
- public Set<ChannelUID> getHighPriorityChannels() {
- return highPriorityChannels;
- }
-
- public Set<ChannelUID> getMediumPriorityChannels() {
- return mediumPriorityChannels;
- }
-
- public Set<ChannelUID> getLowPriorityChannels() {
- return lowPriorityChannels;
- }
-
- /**
- * This method gets the information for specific channel through the {@link SysteminfoInterface}. It uses the
- * channel ID to call the correct method from the {@link SysteminfoInterface} with deviceIndex parameter (in case of
- * multiple devices, for reference see {@link #getDeviceIndex(String)}})
- *
- * @param channelUID the UID of the channel
- * @return State object or null, if there is no information for the device with this index
- */
- private State getInfoForChannel(ChannelUID channelUID) {
- State state = null;
-
- String channelID = channelUID.getId();
- int deviceIndex = getDeviceIndex(channelUID);
-
- logger.trace("Getting state for channel {} with device index {}", channelID, deviceIndex);
-
- // The channelGroup or channel may contain deviceIndex. It must be deleted from the channelID, because otherwise
- // the switch will not find the correct method below.
- // All digits are deleted from the ID, except for CpuLoad channels.
- if (!(CHANNEL_CPU_LOAD_1.equals(channelID) || CHANNEL_CPU_LOAD_5.equals(channelID)
- || CHANNEL_CPU_LOAD_15.equals(channelID))) {
- channelID = channelID.replaceAll("\\d+", "");
- }
-
- try {
- switch (channelID) {
- case CHANNEL_MEMORY_HEAP_AVAILABLE:
- state = new QuantityType<>(Runtime.getRuntime().freeMemory(), Units.BYTE);
- break;
- case CHANNEL_MEMORY_USED_HEAP_PERCENT:
- state = new QuantityType<>((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
- * 100 / Runtime.getRuntime().maxMemory(), Units.PERCENT);
- break;
- case CHANNEL_DISPLAY_INFORMATION:
- state = systeminfo.getDisplayInformation(deviceIndex);
- break;
- case CHANNEL_BATTERY_NAME:
- state = systeminfo.getBatteryName(deviceIndex);
- break;
- case CHANNEL_BATTERY_REMAINING_CAPACITY:
- state = new QuantityType<>(systeminfo.getBatteryRemainingCapacity(deviceIndex), Units.PERCENT);
- break;
- case CHANNEL_BATTERY_REMAINING_TIME:
- state = systeminfo.getBatteryRemainingTime(deviceIndex);
- break;
- case CHANNEL_SENSORS_CPU_TEMPERATURE:
- state = systeminfo.getSensorsCpuTemperature();
- break;
- case CHANNEL_SENOSRS_CPU_VOLTAGE:
- state = systeminfo.getSensorsCpuVoltage();
- break;
- case CHANNEL_SENSORS_FAN_SPEED:
- state = systeminfo.getSensorsFanSpeed(deviceIndex);
- break;
- case CHANNEL_CPU_MAXFREQ:
- state = systeminfo.getCpuMaxFreq();
- break;
- case CHANNEL_CPU_FREQ:
- state = systeminfo.getCpuFreq(deviceIndex);
- break;
- case CHANNEL_CPU_LOAD:
- PercentType cpuLoad = cpuLoadCache.getValue();
- state = (cpuLoad != null) ? new QuantityType<>(cpuLoad, Units.PERCENT) : null;
- break;
- case CHANNEL_CPU_LOAD_1:
- state = systeminfo.getCpuLoad1();
- break;
- case CHANNEL_CPU_LOAD_5:
- state = systeminfo.getCpuLoad5();
- break;
- case CHANNEL_CPU_LOAD_15:
- state = systeminfo.getCpuLoad15();
- break;
- case CHANNEL_CPU_UPTIME:
- state = systeminfo.getCpuUptime();
- break;
- case CHANNEL_CPU_THREADS:
- state = systeminfo.getCpuThreads();
- break;
- case CHANNEL_CPU_DESCRIPTION:
- state = systeminfo.getCpuDescription();
- break;
- case CHANNEL_CPU_NAME:
- state = systeminfo.getCpuName();
- break;
- case CHANNEL_MEMORY_AVAILABLE:
- state = systeminfo.getMemoryAvailable();
- break;
- case CHANNEL_MEMORY_USED:
- state = systeminfo.getMemoryUsed();
- break;
- case CHANNEL_MEMORY_TOTAL:
- state = systeminfo.getMemoryTotal();
- break;
- case CHANNEL_MEMORY_AVAILABLE_PERCENT:
- PercentType memoryAvailablePercent = systeminfo.getMemoryAvailablePercent();
- state = (memoryAvailablePercent != null) ? new QuantityType<>(memoryAvailablePercent, Units.PERCENT)
- : null;
- break;
- case CHANNEL_MEMORY_USED_PERCENT:
- PercentType memoryUsedPercent = systeminfo.getMemoryUsedPercent();
- state = (memoryUsedPercent != null) ? new QuantityType<>(memoryUsedPercent, Units.PERCENT) : null;
- break;
- case CHANNEL_SWAP_AVAILABLE:
- state = systeminfo.getSwapAvailable();
- break;
- case CHANNEL_SWAP_USED:
- state = systeminfo.getSwapUsed();
- break;
- case CHANNEL_SWAP_TOTAL:
- state = systeminfo.getSwapTotal();
- break;
- case CHANNEL_SWAP_AVAILABLE_PERCENT:
- PercentType swapAvailablePercent = systeminfo.getSwapAvailablePercent();
- state = (swapAvailablePercent != null) ? new QuantityType<>(swapAvailablePercent, Units.PERCENT)
- : null;
- break;
- case CHANNEL_SWAP_USED_PERCENT:
- PercentType swapUsedPercent = systeminfo.getSwapUsedPercent();
- state = (swapUsedPercent != null) ? new QuantityType<>(swapUsedPercent, Units.PERCENT) : null;
- break;
- case CHANNEL_DRIVE_MODEL:
- state = systeminfo.getDriveModel(deviceIndex);
- break;
- case CHANNEL_DRIVE_SERIAL:
- state = systeminfo.getDriveSerialNumber(deviceIndex);
- break;
- case CHANNEL_DRIVE_NAME:
- state = systeminfo.getDriveName(deviceIndex);
- break;
- case CHANNEL_STORAGE_NAME:
- state = systeminfo.getStorageName(deviceIndex);
- break;
- case CHANNEL_STORAGE_DESCRIPTION:
- state = systeminfo.getStorageDescription(deviceIndex);
- break;
- case CHANNEL_STORAGE_AVAILABLE:
- state = systeminfo.getStorageAvailable(deviceIndex);
- break;
- case CHANNEL_STORAGE_USED:
- state = systeminfo.getStorageUsed(deviceIndex);
- break;
- case CHANNEL_STORAGE_TOTAL:
- state = systeminfo.getStorageTotal(deviceIndex);
- break;
- case CHANNEL_STORAGE_TYPE:
- state = systeminfo.getStorageType(deviceIndex);
- break;
- case CHANNEL_STORAGE_AVAILABLE_PERCENT:
- PercentType storageAvailablePercent = systeminfo.getStorageAvailablePercent(deviceIndex);
- state = (storageAvailablePercent != null)
- ? new QuantityType<>(storageAvailablePercent, Units.PERCENT)
- : null;
- break;
- case CHANNEL_STORAGE_USED_PERCENT:
- PercentType storageUsedPercent = systeminfo.getStorageUsedPercent(deviceIndex);
- state = (storageUsedPercent != null) ? new QuantityType<>(storageUsedPercent, Units.PERCENT) : null;
- break;
- case CHANNEL_NETWORK_IP:
- state = systeminfo.getNetworkIp(deviceIndex);
- break;
- case CHANNEL_NETWORK_ADAPTER_NAME:
- state = systeminfo.getNetworkDisplayName(deviceIndex);
- break;
- case CHANNEL_NETWORK_NAME:
- state = systeminfo.getNetworkName(deviceIndex);
- break;
- case CHANNEL_NETWORK_MAC:
- state = systeminfo.getNetworkMac(deviceIndex);
- break;
- case CHANNEL_NETWORK_DATA_SENT:
- state = systeminfo.getNetworkDataSent(deviceIndex);
- break;
- case CHANNEL_NETWORK_DATA_RECEIVED:
- state = systeminfo.getNetworkDataReceived(deviceIndex);
- break;
- case CHANNEL_NETWORK_PACKETS_RECEIVED:
- state = systeminfo.getNetworkPacketsReceived(deviceIndex);
- break;
- case CHANNEL_NETWORK_PACKETS_SENT:
- state = systeminfo.getNetworkPacketsSent(deviceIndex);
- break;
- case CHANNEL_PROCESS_LOAD:
- case CHANNEL_CURRENT_PROCESS_LOAD:
- DecimalType processLoad = processLoadCache.putIfAbsentAndGet(deviceIndex,
- () -> getProcessCpuUsage(deviceIndex));
- state = (processLoad != null) ? new QuantityType<>(processLoad, Units.PERCENT) : null;
- break;
- case CHANNEL_PROCESS_MEMORY:
- case CHANNEL_CURRENT_PROCESS_MEMORY:
- state = systeminfo.getProcessMemoryUsage(deviceIndex);
- break;
- case CHANNEL_PROCESS_NAME:
- case CHANNEL_CURRENT_PROCESS_NAME:
- state = systeminfo.getProcessName(deviceIndex);
- break;
- case CHANNEL_PROCESS_PATH:
- case CHANNEL_CURRENT_PROCESS_PATH:
- state = systeminfo.getProcessPath(deviceIndex);
- break;
- case CHANNEL_PROCESS_THREADS:
- case CHANNEL_CURRENT_PROCESS_THREADS:
- state = systeminfo.getProcessThreads(deviceIndex);
- break;
- default:
- logger.debug("Channel with unknown ID: {} !", channelID);
- }
- } catch (DeviceNotFoundException e) {
- logger.warn("No information for channel {} with device index: {}", channelID, deviceIndex);
- } catch (Exception e) {
- logger.debug("Unexpected error occurred while getting system information!", e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/offline.unexpected-error");
- }
- return state != null ? state : UnDefType.UNDEF;
- }
-
- private @Nullable PercentType getSystemCpuLoad() {
- return systeminfo.getSystemCpuLoad();
- }
-
- private @Nullable DecimalType getProcessCpuUsage(int pid) {
- try {
- return systeminfo.getProcessCpuUsage(pid);
- } catch (DeviceNotFoundException e) {
- logger.warn("Process with pid {} does not exist", pid);
- return null;
- }
- }
-
- /**
- * The device index is an optional part of the channelID - the last characters of the groupID. It is used to
- * identify unique device, when more than one devices are available (e.g. local disks with names C:\, D:\, E"\ - the
- * first will have deviceIndex=0, the second deviceIndex=1 ant etc).
- * When no device index is specified, default value of 0 (first device in the list) is returned.
- *
- * @param channelID the ID of the channel
- * @return natural number (number >=0)
- */
- private int getDeviceIndex(ChannelUID channelUID) {
- String channelID = channelUID.getId();
- String channelGroupID = channelUID.getGroupId();
- if (channelGroupID == null) {
- return 0;
- }
-
- if (channelGroupID.contains(CHANNEL_GROUP_PROCESS)) {
- // Only in this case the deviceIndex is part of the channel configuration - PID (Process Identifier)
- int pid = getPID(channelUID);
- logger.debug("Channel with UID {} tracks process with PID: {}", channelUID, pid);
- return pid;
- }
-
- if (channelGroupID.contains(CHANNEL_GROUP_CURRENT_PROCESS)) {
- return systeminfo.getCurrentProcessID();
- }
-
- // First try to get device index in group id, delete all non-digits from id
- if (Character.isDigit(channelGroupID.charAt(channelGroupID.length() - 1))) {
- String deviceIndexPart = channelGroupID.replaceAll("\\D+", "");
- return Integer.parseInt(deviceIndexPart);
- }
-
- // If not found, try to find it in channel id, delete all non-digits from id
- if (Character.isDigit(channelID.charAt(channelID.length() - 1))) {
- String deviceIndexPart = channelID.replaceAll("\\D+", "");
- return Integer.parseInt(deviceIndexPart);
- }
-
- return 0;
- }
-
- /**
- * This method gets the process identifier (PID) for specific process
- *
- * @param channelUID channel unique identifier
- * @return natural number
- */
- private int getPID(ChannelUID channelUID) {
- int pid = 0;
- try {
- Channel channel = this.thing.getChannel(channelUID.getId());
- if (channel != null) {
- Configuration channelProperties = channel.getConfiguration();
- BigDecimal pidValue = (BigDecimal) channelProperties.get(PID_PARAM);
- if (pidValue == null || pidValue.intValue() < 0) {
- throw new IllegalArgumentException("Invalid value for Process Identifier.");
- } else {
- pid = pidValue.intValue();
- }
- } else {
- logger.debug("Channel does not exist! Fall back to default value.");
- }
- } catch (ClassCastException e) {
- logger.debug("Channel configuration cannot be read! Fall back to default value.", e);
- } catch (IllegalArgumentException e) {
- logger.debug("PID (Process Identifier) must be positive number. Fall back to default value. ", e);
- }
- return pid;
- }
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- if (thing.getStatus().equals(ThingStatus.ONLINE)) {
- if (command instanceof RefreshType) {
- logger.debug("Refresh command received for channel {} !", channelUID);
- publishDataForChannel(channelUID);
- } else {
- logger.debug("Unsupported command {} ! Supported commands: REFRESH", command);
- }
- } else {
- logger.debug("Cannot handle command. Thing is not ONLINE.");
- }
- }
-
- private boolean isConfigurationKeyChanged(Configuration currentConfig, Configuration newConfig, String key) {
- Object currentValue = currentConfig.get(key);
- Object newValue = newConfig.get(key);
-
- if (currentValue == null) {
- return (newValue != null);
- }
-
- return !currentValue.equals(newValue);
- }
-
- @Override
- public synchronized void thingUpdated(Thing thing) {
- logger.trace("About to update thing");
- boolean isChannelConfigChanged = false;
-
- List<Channel> channels = thing.getChannels();
-
- for (Channel channel : channels) {
- ChannelUID channelUID = channel.getUID();
- Configuration newChannelConfig = channel.getConfiguration();
- Channel oldChannel = this.thing.getChannel(channelUID.getId());
-
- if (oldChannel == null) {
- logger.warn("Channel with UID {} cannot be updated, as it cannot be found!", channelUID);
- continue;
- }
- Configuration currentChannelConfig = oldChannel.getConfiguration();
-
- if (isConfigurationKeyChanged(currentChannelConfig, newChannelConfig, PRIOIRITY_PARAM)) {
- isChannelConfigChanged = true;
-
- handleChannelConfigurationChange(oldChannel, newChannelConfig, PRIOIRITY_PARAM);
-
- String newPriority = (String) newChannelConfig.get(PRIOIRITY_PARAM);
- changeChannelPriority(channelUID, newPriority);
- }
-
- if (isConfigurationKeyChanged(currentChannelConfig, newChannelConfig, PID_PARAM)) {
- isChannelConfigChanged = true;
- handleChannelConfigurationChange(oldChannel, newChannelConfig, PID_PARAM);
- }
- }
-
- if (!(isInitialized() && isChannelConfigChanged)) {
- super.thingUpdated(thing);
- }
- }
-
- private void handleChannelConfigurationChange(Channel channel, Configuration newConfig, String parameter) {
- Configuration configuration = channel.getConfiguration();
- Object oldValue = configuration.get(parameter);
-
- configuration.put(parameter, newConfig.get(parameter));
-
- Object newValue = newConfig.get(parameter);
- logger.debug("Channel with UID {} has changed its {} from {} to {}", channel.getUID(), parameter, oldValue,
- newValue);
- publishDataForChannel(channel.getUID());
- }
-
- @Override
- protected void changeThingType(ThingTypeUID thingTypeUID, Configuration configuration) {
- storeChannelsConfig();
- super.changeThingType(thingTypeUID, configuration);
- }
-
- private void stopScheduledUpdates() {
- ScheduledFuture<?> localHighPriorityTasks = highPriorityTasks;
- if (localHighPriorityTasks != null) {
- logger.debug("High prioriy tasks will not be run anymore!");
- localHighPriorityTasks.cancel(true);
- }
-
- ScheduledFuture<?> localMediumPriorityTasks = mediumPriorityTasks;
- if (localMediumPriorityTasks != null) {
- logger.debug("Medium prioriy tasks will not be run anymore!");
- localMediumPriorityTasks.cancel(true);
- }
- }
-
- @Override
- public void dispose() {
- stopScheduledUpdates();
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal.model;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.measure.quantity.ElectricPotential;
+import javax.measure.quantity.Frequency;
+import javax.measure.quantity.Temperature;
+import javax.measure.quantity.Time;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.library.dimension.DataAmount;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.ComputerSystem;
+import oshi.hardware.Display;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HWDiskStore;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.hardware.NetworkIF;
+import oshi.hardware.PowerSource;
+import oshi.hardware.Sensors;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OSProcess;
+import oshi.software.os.OperatingSystem;
+import oshi.util.EdidUtil;
+
+/**
+ * This implementation of {@link SystemInfoInterface} is using the open source library OSHI to provide system
+ * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
+ * initializeSystemInfo method
+ * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
+ * CentralProcessor#getSystemSerialNumber()
+ * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
+ * @author Mark Herwege - Add dynamic creation of extra channels
+ * @author Mark Herwege - Use units of measure
+ * @author Mark Herwege - Processor frequency channels
+ *
+ * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
+ */
+@NonNullByDefault
+@Component(service = SystemInfoInterface.class)
+public class OSHISystemInfo implements SystemInfoInterface {
+
+ private final Logger logger = LoggerFactory.getLogger(OSHISystemInfo.class);
+
+ private @NonNullByDefault({}) HardwareAbstractionLayer hal;
+
+ // Dynamic objects (may be queried repeatedly)
+ private @NonNullByDefault({}) GlobalMemory memory;
+ private @NonNullByDefault({}) CentralProcessor cpu;
+ private @NonNullByDefault({}) Sensors sensors;
+
+ // Static objects, should be recreated on each request
+ private @NonNullByDefault({}) ComputerSystem computerSystem;
+ private @NonNullByDefault({}) OperatingSystem operatingSystem;
+ private @NonNullByDefault({}) List<NetworkIF> networks;
+ private @NonNullByDefault({}) List<Display> displays;
+ private @NonNullByDefault({}) List<OSFileStore> fileStores;
+ private @NonNullByDefault({}) List<PowerSource> powerSources;
+ private @NonNullByDefault({}) List<HWDiskStore> drives;
+
+ // Array containing cpu tick info to calculate CPU load, according to oshi doc:
+ // 8 long values representing time spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal states
+ private long[] ticks = new long[8];
+ // Map containing previous process state to calculate load by process
+ private Map<Integer, OSProcess> processTicks = new HashMap<>();
+
+ public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
+
+ /**
+ * Some of the methods used in this constructor execute native code and require execute permissions
+ *
+ */
+ public OSHISystemInfo() {
+ logger.debug("OSHISystemInfo service is created");
+ }
+
+ @Override
+ public void initializeSystemInfo() {
+ logger.debug("OSHISystemInfo service starts initializing");
+
+ SystemInfo systemInfo = new SystemInfo();
+ hal = systemInfo.getHardware();
+
+ // Doesn't need regular update, they may be queried repeatedly
+ memory = hal.getMemory();
+ cpu = hal.getProcessor();
+ sensors = hal.getSensors();
+
+ computerSystem = hal.getComputerSystem();
+ operatingSystem = systemInfo.getOperatingSystem();
+ networks = hal.getNetworkIFs();
+ displays = hal.getDisplays();
+ fileStores = operatingSystem.getFileSystem().getFileStores();
+ powerSources = hal.getPowerSources();
+ drives = hal.getDiskStores();
+ }
+
+ private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
+ if (devices.size() <= index) {
+ throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
+ }
+ return (T) devices.get(index);
+ }
+
+ private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
+ if (devices == null || devices.length <= index) {
+ throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
+ }
+ return devices[index];
+ }
+
+ private OSProcess getProcess(int pid) throws DeviceNotFoundException {
+ OSProcess process = operatingSystem.getProcess(pid);
+ if (process == null) {
+ throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
+ }
+ return process;
+ }
+
+ @Override
+ public StringType getOsFamily() {
+ String osFamily = operatingSystem.getFamily();
+ return new StringType(osFamily);
+ }
+
+ @Override
+ public StringType getOsManufacturer() {
+ String osManufacturer = operatingSystem.getManufacturer();
+ return new StringType(osManufacturer);
+ }
+
+ @Override
+ public StringType getOsVersion() {
+ String osVersion = operatingSystem.getVersionInfo().toString();
+ return new StringType(osVersion);
+ }
+
+ @Override
+ public StringType getCpuName() {
+ String name = cpu.getProcessorIdentifier().getName();
+ return new StringType(name);
+ }
+
+ @Override
+ public StringType getCpuDescription() {
+ String model = cpu.getProcessorIdentifier().getModel();
+ String family = cpu.getProcessorIdentifier().getFamily();
+ String serialNumber = computerSystem.getSerialNumber();
+ String identifier = cpu.getProcessorIdentifier().getIdentifier();
+ String vendor = cpu.getProcessorIdentifier().getVendor();
+ String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
+ String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
+ String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
+ identifier);
+
+ return new StringType(description);
+ }
+
+ @Override
+ public DecimalType getCpuLogicalCores() {
+ int logicalProcessorCount = cpu.getLogicalProcessorCount();
+ return new DecimalType(logicalProcessorCount);
+ }
+
+ @Override
+ public DecimalType getCpuPhysicalCores() {
+ int physicalProcessorCount = cpu.getPhysicalProcessorCount();
+ return new DecimalType(physicalProcessorCount);
+ }
+
+ @Override
+ public @Nullable QuantityType<Frequency> getCpuMaxFreq() {
+ long maxFreq = cpu.getMaxFreq();
+ return maxFreq >= 0 ? new QuantityType<>(maxFreq, Units.HERTZ) : null;
+ }
+
+ @Override
+ public @Nullable QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex) {
+ long freq = cpu.getCurrentFreq()[logicalProcessorIndex];
+ return freq >= 0 ? new QuantityType<>(freq, Units.HERTZ) : null;
+ }
+
+ @Override
+ public QuantityType<DataAmount> getMemoryTotal() {
+ long totalMemory = memory.getTotal();
+ totalMemory = getSizeInMB(totalMemory);
+ return new QuantityType<>(totalMemory, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getMemoryAvailable() {
+ long availableMemory = memory.getAvailable();
+ availableMemory = getSizeInMB(availableMemory);
+ return new QuantityType<>(availableMemory, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getMemoryUsed() {
+ long totalMemory = memory.getTotal();
+ long availableMemory = memory.getAvailable();
+ long usedMemory = totalMemory - availableMemory;
+ usedMemory = getSizeInMB(usedMemory);
+ return new QuantityType<>(usedMemory, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getStorageTotal(int index) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, index);
+ fileStore.updateAttributes();
+ long totalSpace = fileStore.getTotalSpace();
+ totalSpace = getSizeInMB(totalSpace);
+ return new QuantityType<>(totalSpace, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getStorageAvailable(int index) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, index);
+ fileStore.updateAttributes();
+ long freeSpace = fileStore.getUsableSpace();
+ freeSpace = getSizeInMB(freeSpace);
+ return new QuantityType<>(freeSpace, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getStorageUsed(int index) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, index);
+ fileStore.updateAttributes();
+ long totalSpace = fileStore.getTotalSpace();
+ long freeSpace = fileStore.getUsableSpace();
+ long usedSpace = totalSpace - freeSpace;
+ usedSpace = getSizeInMB(usedSpace);
+ return new QuantityType<>(usedSpace, Units.MEBIBYTE);
+ }
+
+ @Override
+ public @Nullable PercentType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, deviceIndex);
+ fileStore.updateAttributes();
+ long totalSpace = fileStore.getTotalSpace();
+ long freeSpace = fileStore.getUsableSpace();
+ if (totalSpace > 0) {
+ double freePercentDecimal = (double) freeSpace / (double) totalSpace;
+ BigDecimal freePercent = getPercentsValue(freePercentDecimal);
+ return new PercentType(freePercent);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable PercentType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, deviceIndex);
+ fileStore.updateAttributes();
+ long totalSpace = fileStore.getTotalSpace();
+ long freeSpace = fileStore.getUsableSpace();
+ long usedSpace = totalSpace - freeSpace;
+ if (totalSpace > 0) {
+ double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
+ BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
+ return new PercentType(usedPercent);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public StringType getStorageName(int index) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, index);
+ String name = fileStore.getName();
+ return new StringType(name);
+ }
+
+ @Override
+ public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, deviceIndex);
+ String type = fileStore.getType();
+ return new StringType(type);
+ }
+
+ @Override
+ public StringType getStorageDescription(int index) throws DeviceNotFoundException {
+ OSFileStore fileStore = getDevice(fileStores, index);
+ String description = fileStore.getDescription();
+ return new StringType(description);
+ }
+
+ @Override
+ public StringType getNetworkIp(int index) throws DeviceNotFoundException {
+ NetworkIF netInterface = getDevice(networks, index);
+ netInterface.updateAttributes();
+ String[] ipAddresses = netInterface.getIPv4addr();
+ String ipv4 = getDevice(ipAddresses, 0);
+ return new StringType(ipv4);
+ }
+
+ @Override
+ public StringType getNetworkName(int index) throws DeviceNotFoundException {
+ NetworkIF netInterface = getDevice(networks, index);
+ String name = netInterface.getName();
+ return new StringType(name);
+ }
+
+ @Override
+ public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
+ NetworkIF netInterface = getDevice(networks, index);
+ String adapterName = netInterface.getDisplayName();
+ return new StringType(adapterName);
+ }
+
+ @Override
+ public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
+ Display display = getDevice(displays, index);
+
+ byte[] edid = display.getEdid();
+ String manufacturer = EdidUtil.getManufacturerID(edid);
+ String product = EdidUtil.getProductID(edid);
+ String serialNumber = EdidUtil.getSerialNo(edid);
+ int width = EdidUtil.getHcm(edid);
+ int height = EdidUtil.getVcm(edid);
+
+ String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
+ String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
+ return new StringType(edidInfo);
+ }
+
+ @Override
+ public @Nullable QuantityType<Temperature> getSensorsCpuTemperature() {
+ BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
+ cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
+ return cpuTemp.signum() == 1 ? new QuantityType<>(cpuTemp, SIUnits.CELSIUS) : null;
+ }
+
+ @Override
+ public @Nullable QuantityType<ElectricPotential> getSensorsCpuVoltage() {
+ BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
+ cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
+ return cpuVoltage.signum() == 1 ? new QuantityType<>(cpuVoltage, Units.VOLT) : null;
+ }
+
+ @Override
+ public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
+ int[] fanSpeeds = sensors.getFanSpeeds();
+ int speed = 0; // 0 means unable to measure speed
+ if (index < fanSpeeds.length) {
+ speed = fanSpeeds[index];
+ } else {
+ throw new DeviceNotFoundException();
+ }
+ return speed > 0 ? new DecimalType(speed) : null;
+ }
+
+ @Override
+ public @Nullable QuantityType<Time> getBatteryRemainingTime(int index) throws DeviceNotFoundException {
+ PowerSource powerSource = getDevice(powerSources, index);
+ powerSource.updateAttributes();
+ double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
+ // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
+ BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
+ return remainingTime.signum() == 1 ? new QuantityType<>(remainingTime, Units.MINUTE) : null;
+ }
+
+ @Override
+ public PercentType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
+ PowerSource powerSource = getDevice(powerSources, index);
+ powerSource.updateAttributes();
+ double remainingCapacity = powerSource.getRemainingCapacityPercent();
+ BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
+ return new PercentType(remainingCapacityPercents);
+ }
+
+ @Override
+ public StringType getBatteryName(int index) throws DeviceNotFoundException {
+ PowerSource powerSource = getDevice(powerSources, index);
+ String name = powerSource.getName();
+ return new StringType(name);
+ }
+
+ @Override
+ public @Nullable PercentType getMemoryAvailablePercent() {
+ long availableMemory = memory.getAvailable();
+ long totalMemory = memory.getTotal();
+ if (totalMemory > 0) {
+ double freePercentDecimal = (double) availableMemory / (double) totalMemory;
+ BigDecimal freePercent = getPercentsValue(freePercentDecimal);
+ return new PercentType(freePercent);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable PercentType getMemoryUsedPercent() {
+ long availableMemory = memory.getAvailable();
+ long totalMemory = memory.getTotal();
+ long usedMemory = totalMemory - availableMemory;
+ if (totalMemory > 0) {
+ double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
+ BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
+ return new PercentType(usedPercent);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
+ HWDiskStore drive = getDevice(drives, deviceIndex);
+ String name = drive.getName();
+ return new StringType(name);
+ }
+
+ @Override
+ public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
+ HWDiskStore drive = getDevice(drives, deviceIndex);
+ String model = drive.getModel();
+ return new StringType(model);
+ }
+
+ @Override
+ public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
+ HWDiskStore drive = getDevice(drives, deviceIndex);
+ String serialNumber = drive.getSerial();
+ return new StringType(serialNumber);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getSwapTotal() {
+ long swapTotal = memory.getVirtualMemory().getSwapTotal();
+ swapTotal = getSizeInMB(swapTotal);
+ return new QuantityType<>(swapTotal, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getSwapAvailable() {
+ long swapTotal = memory.getVirtualMemory().getSwapTotal();
+ long swapUsed = memory.getVirtualMemory().getSwapUsed();
+ long swapAvailable = swapTotal - swapUsed;
+ swapAvailable = getSizeInMB(swapAvailable);
+ return new QuantityType<>(swapAvailable, Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getSwapUsed() {
+ long swapUsed = memory.getVirtualMemory().getSwapUsed();
+ swapUsed = getSizeInMB(swapUsed);
+ return new QuantityType<>(swapUsed, Units.MEBIBYTE);
+ }
+
+ @Override
+ public @Nullable PercentType getSwapAvailablePercent() {
+ long swapTotal = memory.getVirtualMemory().getSwapTotal();
+ long swapUsed = memory.getVirtualMemory().getSwapUsed();
+ long swapAvailable = swapTotal - swapUsed;
+ if (swapTotal > 0) {
+ double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
+ BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
+ return new PercentType(swapAvailablePercent);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable PercentType getSwapUsedPercent() {
+ long swapTotal = memory.getVirtualMemory().getSwapTotal();
+ long swapUsed = memory.getVirtualMemory().getSwapUsed();
+ if (swapTotal > 0) {
+ double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
+ BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
+ return new PercentType(swapUsedPercent);
+ } else {
+ return null;
+ }
+ }
+
+ private long getSizeInMB(long sizeInBytes) {
+ return Math.round(sizeInBytes / (1024D * 1024));
+ }
+
+ private BigDecimal getPercentsValue(double decimalFraction) {
+ BigDecimal result = new BigDecimal(decimalFraction * 100);
+ result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
+ return result;
+ }
+
+ private BigDecimal getTimeInMinutes(double timeInSeconds) {
+ BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
+ timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
+ return timeInMinutes;
+ }
+
+ @Override
+ public @Nullable PercentType getSystemCpuLoad() {
+ PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
+ : null;
+ ticks = cpu.getSystemCpuLoadTicks();
+ return load;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This information is available only on Mac and Linux OS.
+ */
+ @Override
+ public @Nullable DecimalType getCpuLoad1() {
+ BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
+ return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This information is available only on Mac and Linux OS.
+ */
+ @Override
+ public @Nullable DecimalType getCpuLoad5() {
+ BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
+ return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This information is available only on Mac and Linux OS.
+ */
+ @Override
+ public @Nullable DecimalType getCpuLoad15() {
+ BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
+ return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
+ }
+
+ private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
+ // This parameter is specified in OSHI Javadoc
+ int index;
+ switch (timeInMinutes) {
+ case 1:
+ index = 0;
+ break;
+ case 5:
+ index = 1;
+ break;
+ case 15:
+ index = 2;
+ break;
+ default:
+ index = 2;
+ }
+ double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
+ BigDecimal result = new BigDecimal(processorLoads[index]);
+ result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
+ return result;
+ }
+
+ @Override
+ public QuantityType<Time> getCpuUptime() {
+ long seconds = operatingSystem.getSystemUptime();
+ return new QuantityType<>(getTimeInMinutes(seconds), Units.MINUTE);
+ }
+
+ @Override
+ public DecimalType getCpuThreads() {
+ int threadCount = operatingSystem.getThreadCount();
+ return new DecimalType(threadCount);
+ }
+
+ @Override
+ public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
+ NetworkIF network = getDevice(networks, networkIndex);
+ String mac = network.getMacaddr();
+ return new StringType(mac);
+ }
+
+ @Override
+ public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
+ NetworkIF network = getDevice(networks, networkIndex);
+ network.updateAttributes();
+ long packRecv = network.getPacketsRecv();
+ return new DecimalType(packRecv);
+ }
+
+ @Override
+ public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
+ NetworkIF network = getDevice(networks, networkIndex);
+ network.updateAttributes();
+ long packSent = network.getPacketsSent();
+ return new DecimalType(packSent);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
+ NetworkIF network = getDevice(networks, networkIndex);
+ network.updateAttributes();
+ long bytesSent = network.getBytesSent();
+ return new QuantityType<>(getSizeInMB(bytesSent), Units.MEBIBYTE);
+ }
+
+ @Override
+ public QuantityType<DataAmount> getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
+ NetworkIF network = getDevice(networks, networkIndex);
+ network.updateAttributes();
+ long bytesRecv = network.getBytesRecv();
+ return new QuantityType<>(getSizeInMB(bytesRecv), Units.MEBIBYTE);
+ }
+
+ @Override
+ public int getCurrentProcessID() {
+ return operatingSystem.getProcessId();
+ }
+
+ @Override
+ public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
+ if (pid > 0) {
+ OSProcess process = getProcess(pid);
+ String name = process.getName();
+ return new StringType(name);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
+ if (pid > 0) {
+ OSProcess process = getProcess(pid);
+ DecimalType load = (processTicks.containsKey(pid))
+ ? new DecimalType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
+ : null;
+ processTicks.put(pid, process);
+ return load;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable QuantityType<DataAmount> getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
+ if (pid > 0) {
+ OSProcess process = getProcess(pid);
+ long memortInBytes = process.getResidentSetSize();
+ long memoryInMB = getSizeInMB(memortInBytes);
+ return new QuantityType<>(memoryInMB, Units.MEBIBYTE);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
+ if (pid > 0) {
+ OSProcess process = getProcess(pid);
+ String path = process.getPath();
+ return new StringType(path);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
+ if (pid > 0) {
+ OSProcess process = getProcess(pid);
+ int threadCount = process.getThreadCount();
+ return new DecimalType(threadCount);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public int getNetworkIFCount() {
+ return networks.size();
+ }
+
+ @Override
+ public int getDisplayCount() {
+ return displays.size();
+ }
+
+ @Override
+ public int getFileOSStoreCount() {
+ return fileStores.size();
+ }
+
+ @Override
+ public int getPowerSourceCount() {
+ return powerSources.size();
+ }
+
+ @Override
+ public int getDriveCount() {
+ return drives.size();
+ }
+
+ @Override
+ public int getFanCount() {
+ return sensors.getFanSpeeds().length;
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal.model;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.measure.quantity.ElectricPotential;
-import javax.measure.quantity.Frequency;
-import javax.measure.quantity.Temperature;
-import javax.measure.quantity.Time;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.library.dimension.DataAmount;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.osgi.service.component.annotations.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import oshi.SystemInfo;
-import oshi.hardware.CentralProcessor;
-import oshi.hardware.ComputerSystem;
-import oshi.hardware.Display;
-import oshi.hardware.GlobalMemory;
-import oshi.hardware.HWDiskStore;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.hardware.NetworkIF;
-import oshi.hardware.PowerSource;
-import oshi.hardware.Sensors;
-import oshi.software.os.OSFileStore;
-import oshi.software.os.OSProcess;
-import oshi.software.os.OperatingSystem;
-import oshi.util.EdidUtil;
-
-/**
- * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
- * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
- * initializeSysteminfo method
- * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
- * CentralProcessor#getSystemSerialNumber()
- * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
- * @author Mark Herwege - Add dynamic creation of extra channels
- * @author Mark Herwege - Use units of measure
- * @author Mark Herwege - Processor frequency channels
- *
- * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
- */
-@NonNullByDefault
-@Component(service = SysteminfoInterface.class)
-public class OSHISysteminfo implements SysteminfoInterface {
-
- private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
-
- private @NonNullByDefault({}) HardwareAbstractionLayer hal;
-
- // Dynamic objects (may be queried repeatedly)
- private @NonNullByDefault({}) GlobalMemory memory;
- private @NonNullByDefault({}) CentralProcessor cpu;
- private @NonNullByDefault({}) Sensors sensors;
-
- // Static objects, should be recreated on each request
- private @NonNullByDefault({}) ComputerSystem computerSystem;
- private @NonNullByDefault({}) OperatingSystem operatingSystem;
- private @NonNullByDefault({}) List<NetworkIF> networks;
- private @NonNullByDefault({}) List<Display> displays;
- private @NonNullByDefault({}) List<OSFileStore> fileStores;
- private @NonNullByDefault({}) List<PowerSource> powerSources;
- private @NonNullByDefault({}) List<HWDiskStore> drives;
-
- // Array containing cpu tick info to calculate CPU load, according to oshi doc:
- // 8 long values representing time spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal states
- private long[] ticks = new long[8];
- // Map containing previous process state to calculate load by process
- private Map<Integer, OSProcess> processTicks = new HashMap<>();
-
- public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
-
- /**
- * Some of the methods used in this constructor execute native code and require execute permissions
- *
- */
- public OSHISysteminfo() {
- logger.debug("OSHISysteminfo service is created");
- }
-
- @Override
- public void initializeSysteminfo() {
- logger.debug("OSHISysteminfo service starts initializing");
-
- SystemInfo systemInfo = new SystemInfo();
- hal = systemInfo.getHardware();
-
- // Doesn't need regular update, they may be queried repeatedly
- memory = hal.getMemory();
- cpu = hal.getProcessor();
- sensors = hal.getSensors();
-
- computerSystem = hal.getComputerSystem();
- operatingSystem = systemInfo.getOperatingSystem();
- networks = hal.getNetworkIFs();
- displays = hal.getDisplays();
- fileStores = operatingSystem.getFileSystem().getFileStores();
- powerSources = hal.getPowerSources();
- drives = hal.getDiskStores();
- }
-
- private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
- if (devices.size() <= index) {
- throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
- }
- return (T) devices.get(index);
- }
-
- private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
- if (devices == null || devices.length <= index) {
- throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
- }
- return devices[index];
- }
-
- private OSProcess getProcess(int pid) throws DeviceNotFoundException {
- OSProcess process = operatingSystem.getProcess(pid);
- if (process == null) {
- throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
- }
- return process;
- }
-
- @Override
- public StringType getOsFamily() {
- String osFamily = operatingSystem.getFamily();
- return new StringType(osFamily);
- }
-
- @Override
- public StringType getOsManufacturer() {
- String osManufacturer = operatingSystem.getManufacturer();
- return new StringType(osManufacturer);
- }
-
- @Override
- public StringType getOsVersion() {
- String osVersion = operatingSystem.getVersionInfo().toString();
- return new StringType(osVersion);
- }
-
- @Override
- public StringType getCpuName() {
- String name = cpu.getProcessorIdentifier().getName();
- return new StringType(name);
- }
-
- @Override
- public StringType getCpuDescription() {
- String model = cpu.getProcessorIdentifier().getModel();
- String family = cpu.getProcessorIdentifier().getFamily();
- String serialNumber = computerSystem.getSerialNumber();
- String identifier = cpu.getProcessorIdentifier().getIdentifier();
- String vendor = cpu.getProcessorIdentifier().getVendor();
- String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
- String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
- String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
- identifier);
-
- return new StringType(description);
- }
-
- @Override
- public DecimalType getCpuLogicalCores() {
- int logicalProcessorCount = cpu.getLogicalProcessorCount();
- return new DecimalType(logicalProcessorCount);
- }
-
- @Override
- public DecimalType getCpuPhysicalCores() {
- int physicalProcessorCount = cpu.getPhysicalProcessorCount();
- return new DecimalType(physicalProcessorCount);
- }
-
- @Override
- public @Nullable QuantityType<Frequency> getCpuMaxFreq() {
- long maxFreq = cpu.getMaxFreq();
- return maxFreq >= 0 ? new QuantityType<>(maxFreq, Units.HERTZ) : null;
- }
-
- @Override
- public @Nullable QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex) {
- long freq = cpu.getCurrentFreq()[logicalProcessorIndex];
- return freq >= 0 ? new QuantityType<>(freq, Units.HERTZ) : null;
- }
-
- @Override
- public QuantityType<DataAmount> getMemoryTotal() {
- long totalMemory = memory.getTotal();
- totalMemory = getSizeInMB(totalMemory);
- return new QuantityType<>(totalMemory, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getMemoryAvailable() {
- long availableMemory = memory.getAvailable();
- availableMemory = getSizeInMB(availableMemory);
- return new QuantityType<>(availableMemory, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getMemoryUsed() {
- long totalMemory = memory.getTotal();
- long availableMemory = memory.getAvailable();
- long usedMemory = totalMemory - availableMemory;
- usedMemory = getSizeInMB(usedMemory);
- return new QuantityType<>(usedMemory, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getStorageTotal(int index) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, index);
- fileStore.updateAttributes();
- long totalSpace = fileStore.getTotalSpace();
- totalSpace = getSizeInMB(totalSpace);
- return new QuantityType<>(totalSpace, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getStorageAvailable(int index) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, index);
- fileStore.updateAttributes();
- long freeSpace = fileStore.getUsableSpace();
- freeSpace = getSizeInMB(freeSpace);
- return new QuantityType<>(freeSpace, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getStorageUsed(int index) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, index);
- fileStore.updateAttributes();
- long totalSpace = fileStore.getTotalSpace();
- long freeSpace = fileStore.getUsableSpace();
- long usedSpace = totalSpace - freeSpace;
- usedSpace = getSizeInMB(usedSpace);
- return new QuantityType<>(usedSpace, Units.MEBIBYTE);
- }
-
- @Override
- public @Nullable PercentType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, deviceIndex);
- fileStore.updateAttributes();
- long totalSpace = fileStore.getTotalSpace();
- long freeSpace = fileStore.getUsableSpace();
- if (totalSpace > 0) {
- double freePercentDecimal = (double) freeSpace / (double) totalSpace;
- BigDecimal freePercent = getPercentsValue(freePercentDecimal);
- return new PercentType(freePercent);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable PercentType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, deviceIndex);
- fileStore.updateAttributes();
- long totalSpace = fileStore.getTotalSpace();
- long freeSpace = fileStore.getUsableSpace();
- long usedSpace = totalSpace - freeSpace;
- if (totalSpace > 0) {
- double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
- BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
- return new PercentType(usedPercent);
- } else {
- return null;
- }
- }
-
- @Override
- public StringType getStorageName(int index) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, index);
- String name = fileStore.getName();
- return new StringType(name);
- }
-
- @Override
- public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, deviceIndex);
- String type = fileStore.getType();
- return new StringType(type);
- }
-
- @Override
- public StringType getStorageDescription(int index) throws DeviceNotFoundException {
- OSFileStore fileStore = getDevice(fileStores, index);
- String description = fileStore.getDescription();
- return new StringType(description);
- }
-
- @Override
- public StringType getNetworkIp(int index) throws DeviceNotFoundException {
- NetworkIF netInterface = getDevice(networks, index);
- netInterface.updateAttributes();
- String[] ipAddresses = netInterface.getIPv4addr();
- String ipv4 = getDevice(ipAddresses, 0);
- return new StringType(ipv4);
- }
-
- @Override
- public StringType getNetworkName(int index) throws DeviceNotFoundException {
- NetworkIF netInterface = getDevice(networks, index);
- String name = netInterface.getName();
- return new StringType(name);
- }
-
- @Override
- public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
- NetworkIF netInterface = getDevice(networks, index);
- String adapterName = netInterface.getDisplayName();
- return new StringType(adapterName);
- }
-
- @Override
- public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
- Display display = getDevice(displays, index);
-
- byte[] edid = display.getEdid();
- String manufacturer = EdidUtil.getManufacturerID(edid);
- String product = EdidUtil.getProductID(edid);
- String serialNumber = EdidUtil.getSerialNo(edid);
- int width = EdidUtil.getHcm(edid);
- int height = EdidUtil.getVcm(edid);
-
- String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
- String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
- return new StringType(edidInfo);
- }
-
- @Override
- public @Nullable QuantityType<Temperature> getSensorsCpuTemperature() {
- BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
- cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
- return cpuTemp.signum() == 1 ? new QuantityType<>(cpuTemp, SIUnits.CELSIUS) : null;
- }
-
- @Override
- public @Nullable QuantityType<ElectricPotential> getSensorsCpuVoltage() {
- BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
- cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
- return cpuVoltage.signum() == 1 ? new QuantityType<>(cpuVoltage, Units.VOLT) : null;
- }
-
- @Override
- public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
- int[] fanSpeeds = sensors.getFanSpeeds();
- int speed = 0; // 0 means unable to measure speed
- if (index < fanSpeeds.length) {
- speed = fanSpeeds[index];
- } else {
- throw new DeviceNotFoundException();
- }
- return speed > 0 ? new DecimalType(speed) : null;
- }
-
- @Override
- public @Nullable QuantityType<Time> getBatteryRemainingTime(int index) throws DeviceNotFoundException {
- PowerSource powerSource = getDevice(powerSources, index);
- powerSource.updateAttributes();
- double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
- // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
- BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
- return remainingTime.signum() == 1 ? new QuantityType<>(remainingTime, Units.MINUTE) : null;
- }
-
- @Override
- public PercentType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
- PowerSource powerSource = getDevice(powerSources, index);
- powerSource.updateAttributes();
- double remainingCapacity = powerSource.getRemainingCapacityPercent();
- BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
- return new PercentType(remainingCapacityPercents);
- }
-
- @Override
- public StringType getBatteryName(int index) throws DeviceNotFoundException {
- PowerSource powerSource = getDevice(powerSources, index);
- String name = powerSource.getName();
- return new StringType(name);
- }
-
- @Override
- public @Nullable PercentType getMemoryAvailablePercent() {
- long availableMemory = memory.getAvailable();
- long totalMemory = memory.getTotal();
- if (totalMemory > 0) {
- double freePercentDecimal = (double) availableMemory / (double) totalMemory;
- BigDecimal freePercent = getPercentsValue(freePercentDecimal);
- return new PercentType(freePercent);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable PercentType getMemoryUsedPercent() {
- long availableMemory = memory.getAvailable();
- long totalMemory = memory.getTotal();
- long usedMemory = totalMemory - availableMemory;
- if (totalMemory > 0) {
- double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
- BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
- return new PercentType(usedPercent);
- } else {
- return null;
- }
- }
-
- @Override
- public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
- HWDiskStore drive = getDevice(drives, deviceIndex);
- String name = drive.getName();
- return new StringType(name);
- }
-
- @Override
- public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
- HWDiskStore drive = getDevice(drives, deviceIndex);
- String model = drive.getModel();
- return new StringType(model);
- }
-
- @Override
- public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
- HWDiskStore drive = getDevice(drives, deviceIndex);
- String serialNumber = drive.getSerial();
- return new StringType(serialNumber);
- }
-
- @Override
- public QuantityType<DataAmount> getSwapTotal() {
- long swapTotal = memory.getVirtualMemory().getSwapTotal();
- swapTotal = getSizeInMB(swapTotal);
- return new QuantityType<>(swapTotal, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getSwapAvailable() {
- long swapTotal = memory.getVirtualMemory().getSwapTotal();
- long swapUsed = memory.getVirtualMemory().getSwapUsed();
- long swapAvailable = swapTotal - swapUsed;
- swapAvailable = getSizeInMB(swapAvailable);
- return new QuantityType<>(swapAvailable, Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getSwapUsed() {
- long swapUsed = memory.getVirtualMemory().getSwapUsed();
- swapUsed = getSizeInMB(swapUsed);
- return new QuantityType<>(swapUsed, Units.MEBIBYTE);
- }
-
- @Override
- public @Nullable PercentType getSwapAvailablePercent() {
- long swapTotal = memory.getVirtualMemory().getSwapTotal();
- long swapUsed = memory.getVirtualMemory().getSwapUsed();
- long swapAvailable = swapTotal - swapUsed;
- if (swapTotal > 0) {
- double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
- BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
- return new PercentType(swapAvailablePercent);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable PercentType getSwapUsedPercent() {
- long swapTotal = memory.getVirtualMemory().getSwapTotal();
- long swapUsed = memory.getVirtualMemory().getSwapUsed();
- if (swapTotal > 0) {
- double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
- BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
- return new PercentType(swapUsedPercent);
- } else {
- return null;
- }
- }
-
- private long getSizeInMB(long sizeInBytes) {
- return Math.round(sizeInBytes / (1024D * 1024));
- }
-
- private BigDecimal getPercentsValue(double decimalFraction) {
- BigDecimal result = new BigDecimal(decimalFraction * 100);
- result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
- return result;
- }
-
- private BigDecimal getTimeInMinutes(double timeInSeconds) {
- BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
- timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
- return timeInMinutes;
- }
-
- @Override
- public @Nullable PercentType getSystemCpuLoad() {
- PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
- : null;
- ticks = cpu.getSystemCpuLoadTicks();
- return load;
- }
-
- /**
- * {@inheritDoc}
- *
- * This information is available only on Mac and Linux OS.
- */
- @Override
- public @Nullable DecimalType getCpuLoad1() {
- BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
- return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
- }
-
- /**
- * {@inheritDoc}
- *
- * This information is available only on Mac and Linux OS.
- */
- @Override
- public @Nullable DecimalType getCpuLoad5() {
- BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
- return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
- }
-
- /**
- * {@inheritDoc}
- *
- * This information is available only on Mac and Linux OS.
- */
- @Override
- public @Nullable DecimalType getCpuLoad15() {
- BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
- return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
- }
-
- private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
- // This parameter is specified in OSHI Javadoc
- int index;
- switch (timeInMinutes) {
- case 1:
- index = 0;
- break;
- case 5:
- index = 1;
- break;
- case 15:
- index = 2;
- break;
- default:
- index = 2;
- }
- double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
- BigDecimal result = new BigDecimal(processorLoads[index]);
- result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
- return result;
- }
-
- @Override
- public QuantityType<Time> getCpuUptime() {
- long seconds = operatingSystem.getSystemUptime();
- return new QuantityType<>(getTimeInMinutes(seconds), Units.MINUTE);
- }
-
- @Override
- public DecimalType getCpuThreads() {
- int threadCount = operatingSystem.getThreadCount();
- return new DecimalType(threadCount);
- }
-
- @Override
- public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
- NetworkIF network = getDevice(networks, networkIndex);
- String mac = network.getMacaddr();
- return new StringType(mac);
- }
-
- @Override
- public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
- NetworkIF network = getDevice(networks, networkIndex);
- network.updateAttributes();
- long packRecv = network.getPacketsRecv();
- return new DecimalType(packRecv);
- }
-
- @Override
- public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
- NetworkIF network = getDevice(networks, networkIndex);
- network.updateAttributes();
- long packSent = network.getPacketsSent();
- return new DecimalType(packSent);
- }
-
- @Override
- public QuantityType<DataAmount> getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
- NetworkIF network = getDevice(networks, networkIndex);
- network.updateAttributes();
- long bytesSent = network.getBytesSent();
- return new QuantityType<>(getSizeInMB(bytesSent), Units.MEBIBYTE);
- }
-
- @Override
- public QuantityType<DataAmount> getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
- NetworkIF network = getDevice(networks, networkIndex);
- network.updateAttributes();
- long bytesRecv = network.getBytesRecv();
- return new QuantityType<>(getSizeInMB(bytesRecv), Units.MEBIBYTE);
- }
-
- @Override
- public int getCurrentProcessID() {
- return operatingSystem.getProcessId();
- }
-
- @Override
- public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
- if (pid > 0) {
- OSProcess process = getProcess(pid);
- String name = process.getName();
- return new StringType(name);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
- if (pid > 0) {
- OSProcess process = getProcess(pid);
- DecimalType load = (processTicks.containsKey(pid))
- ? new DecimalType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
- : null;
- processTicks.put(pid, process);
- return load;
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable QuantityType<DataAmount> getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
- if (pid > 0) {
- OSProcess process = getProcess(pid);
- long memortInBytes = process.getResidentSetSize();
- long memoryInMB = getSizeInMB(memortInBytes);
- return new QuantityType<>(memoryInMB, Units.MEBIBYTE);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
- if (pid > 0) {
- OSProcess process = getProcess(pid);
- String path = process.getPath();
- return new StringType(path);
- } else {
- return null;
- }
- }
-
- @Override
- public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
- if (pid > 0) {
- OSProcess process = getProcess(pid);
- int threadCount = process.getThreadCount();
- return new DecimalType(threadCount);
- } else {
- return null;
- }
- }
-
- @Override
- public int getNetworkIFCount() {
- return networks.size();
- }
-
- @Override
- public int getDisplayCount() {
- return displays.size();
- }
-
- @Override
- public int getFileOSStoreCount() {
- return fileStores.size();
- }
-
- @Override
- public int getPowerSourceCount() {
- return powerSources.size();
- }
-
- @Override
- public int getDriveCount() {
- return drives.size();
- }
-
- @Override
- public int getFanCount() {
- return sensors.getFanSpeeds().length;
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.internal.model;
+
+import javax.measure.quantity.ElectricPotential;
+import javax.measure.quantity.Frequency;
+import javax.measure.quantity.Temperature;
+import javax.measure.quantity.Time;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.library.dimension.DataAmount;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+
+/**
+ * {@link SystemInfoInterface} defines the methods needed to provide this binding with the required system information.
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Wouter Born - Add null annotations
+ * @author Mark Herwege - Add dynamic creation of extra channels
+ * @author Mark Herwege - Use units of measure
+ * @author Mark Herwege - Processor frequency channels
+ */
+@NonNullByDefault
+public interface SystemInfoInterface {
+
+ /**
+ * Initialize logic for the SystemInfo implementation
+ */
+ void initializeSystemInfo();
+
+ // Operating system info
+ /**
+ * Get the Family of the operating system /e.g. Windows, Unix,.../
+ */
+ StringType getOsFamily();
+
+ /**
+ * Get the manufacturer of the operating system
+ */
+ StringType getOsManufacturer();
+
+ /**
+ * Get the version of the operating system
+ *
+ * @return
+ */
+ StringType getOsVersion();
+
+ // CPU info
+ /**
+ * Get the name of the CPU
+ */
+ StringType getCpuName();
+
+ /**
+ * Get description about the CPU e.g (model, family, vendor, serial number, identifier, architecture(32bit or
+ * 64bit))
+ */
+ StringType getCpuDescription();
+
+ /**
+ * Get the number of logical CPUs/cores available for processing.
+ */
+ DecimalType getCpuLogicalCores();
+
+ /**
+ * Get the number of physical CPUs/cores available for processing.
+ */
+ DecimalType getCpuPhysicalCores();
+
+ /**
+ * Get the maximum CPU frequency of the processor.
+ */
+ @Nullable
+ QuantityType<Frequency> getCpuMaxFreq();
+
+ /**
+ * Get the current CPU frequency of a logical processor.
+ */
+ @Nullable
+ QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex);
+
+ /**
+ * Returns the system cpu load.
+ *
+ * @return the system cpu load between 0 and 100% or null, if no information is available
+ */
+ @Nullable
+ PercentType getSystemCpuLoad();
+
+ /**
+ * Returns the system load average for the last minute.
+ *
+ * @return the load as a number of processes or null, if no information is available
+ */
+ @Nullable
+ DecimalType getCpuLoad1();
+
+ /**
+ * Returns the system load average for the last 5 minutes.
+ *
+ * @return the load as number of processes or null, if no information is available
+ */
+ @Nullable
+ DecimalType getCpuLoad5();
+
+ /**
+ * Returns the system load average for the last 15 minutes.
+ *
+ * @return the load as number of processes or null, if no information is available
+ */
+ @Nullable
+ DecimalType getCpuLoad15();
+
+ /**
+ * Get the System uptime (time since boot).
+ *
+ * @return time since boot
+ */
+ QuantityType<Time> getCpuUptime();
+
+ /**
+ * Get the number of threads currently running
+ *
+ * @return number of threads
+ */
+ DecimalType getCpuThreads();
+
+ // Memory info
+ /**
+ * Returns total size of memory
+ *
+ * @return memory size
+ */
+ QuantityType<DataAmount> getMemoryTotal();
+
+ /**
+ * Returns available size of memory
+ *
+ * @return memory size
+ */
+ QuantityType<DataAmount> getMemoryAvailable();
+
+ /**
+ * Returns used size of memory
+ *
+ * @return memory size
+ */
+ QuantityType<DataAmount> getMemoryUsed();
+
+ /**
+ * Percents of available memory on the machine
+ *
+ * @return percent of available memory or null, if no information is available
+ */
+ @Nullable
+ PercentType getMemoryAvailablePercent();
+
+ /**
+ * Percents of used memory on the machine
+ *
+ * @return percent of used memory or null, if no information is available
+ */
+ @Nullable
+ PercentType getMemoryUsedPercent();
+
+ // Swap memory info
+ /**
+ * Returns total size of swap memory
+ *
+ * @return memory size or 0, if there is no swap memory
+ */
+ QuantityType<DataAmount> getSwapTotal();
+
+ /**
+ * Returns available size swap of memory
+ *
+ * @return memory size or 0, if no there is no swap memory
+ */
+ QuantityType<DataAmount> getSwapAvailable();
+
+ /**
+ * Returns used size of swap memory
+ *
+ * @return memory size or 0, if no there is no swap memory
+ */
+ QuantityType<DataAmount> getSwapUsed();
+
+ /**
+ * Percents of available swap memory on the machine
+ *
+ * @return percent of available memory or null, if no there is no swap memory
+ */
+ @Nullable
+ PercentType getSwapAvailablePercent();
+
+ /**
+ * Percents of used swap memory on the machine
+ *
+ * @return percent of used memory or null, if no there is no swap memory
+ */
+ @Nullable
+ PercentType getSwapUsedPercent();
+
+ // Storage info
+ /**
+ * Returns the total space of the logical storage volume.
+ *
+ * @param deviceIndex - the index of the logical volume
+ * @return storage size
+ * @throws DeviceNotFoundException
+ */
+ QuantityType<DataAmount> getStorageTotal(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Returns the available storage space on the logical storage volume
+ *
+ * @param deviceIndex - the index of the logical volume
+ * @return storage size
+ * @throws DeviceNotFoundException
+ */
+ QuantityType<DataAmount> getStorageAvailable(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the used storage space on the logical storage volume
+ *
+ * @param deviceIndex - the index of the logical volume
+ * @return storage size
+ * @throws DeviceNotFoundException
+ */
+ QuantityType<DataAmount> getStorageUsed(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the percent of available storage on the logical volume
+ *
+ * @param deviceIndex - the index of the logical volume
+ * @return percent of available storage or null
+ * @throws DeviceNotFoundException
+ */
+ @Nullable
+ PercentType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the percent of used storage on the logical volume
+ *
+ * @param deviceIndex - the index of the logical volume
+ * @return percent of used storage or null
+ * @throws DeviceNotFoundException
+ */
+ @Nullable
+ PercentType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the name of the logical storage volume
+ *
+ * @throws DeviceNotFoundException
+ */
+ StringType getStorageName(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the type of the logical storage volume (e.g. NTFS, FAT32)
+ *
+ * @throws DeviceNotFoundException
+ */
+ StringType getStorageType(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the description of the logical storage volume
+ *
+ * @throws DeviceNotFoundException
+ */
+ StringType getStorageDescription(int deviceIndex) throws DeviceNotFoundException;
+
+ // Hardware drive info
+ /**
+ * Gets the name of the physical storage drive
+ *
+ * @param deviceIndex - index of the storage drive
+ * @throws DeviceNotFoundException
+ */
+ StringType getDriveName(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the model of the physical storage drive
+ *
+ * @param deviceIndex - index of the storage drive
+ * @throws DeviceNotFoundException
+ */
+ StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the serial number of the physical storage drive
+ *
+ * @param deviceIndex - index of the storage drive
+ * @throws DeviceNotFoundException
+ */
+ StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException;
+
+ // Network info
+ /**
+ * Get the Host IP address of the network.
+ *
+ * @param networkIndex - the index of the network
+ * @return 32-bit IPv4 address
+ * @throws DeviceNotFoundException
+ */
+ StringType getNetworkIp(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get the name of this network.
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ StringType getNetworkName(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * The description of the network. On some platforms, this is identical to the name.
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ StringType getNetworkDisplayName(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Gets the MAC Address of the network.
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get number of packets received
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get number of packets sent
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get data sent for this network
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ QuantityType<DataAmount> getNetworkDataSent(int networkIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get data received for this network
+ *
+ * @param networkIndex - the index of the network
+ * @throws DeviceNotFoundException
+ */
+ QuantityType<DataAmount> getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException;
+
+ // Display info
+ /**
+ * Get information about the display device as product number, manufacturer, serial number, width and height in cm";
+ *
+ * @param deviceIndex - the index of the display device
+ * @throws DeviceNotFoundException
+ */
+ StringType getDisplayInformation(int deviceIndex) throws DeviceNotFoundException;
+
+ // Sensors info
+ /**
+ * Get the information from the CPU temperature sensors.
+ *
+ * @return Temperature if available, null otherwise.
+ */
+ @Nullable
+ QuantityType<Temperature> getSensorsCpuTemperature();
+
+ /**
+ * Get the information for the CPU voltage.
+ *
+ * @return Voltage if available, null otherwise.
+ */
+ @Nullable
+ QuantityType<ElectricPotential> getSensorsCpuVoltage();
+
+ /**
+ * Get fan speed
+ *
+ * @param deviceIndex
+ * @return Speed in rpm or null if unable to measure fan speed
+ * @throws DeviceNotFoundException
+ */
+ @Nullable
+ DecimalType getSensorsFanSpeed(int deviceIndex) throws DeviceNotFoundException;
+
+ // Battery info
+ /**
+ * Get estimated time remaining for the power source.
+ *
+ * @param deviceIndex
+ * @return duration remaining charge or null, if the time is estimated as unlimited
+ * @throws DeviceNotFoundException
+ */
+ @Nullable
+ QuantityType<Time> getBatteryRemainingTime(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Battery remaining capacity.
+ *
+ * @param deviceIndex
+ * @return percentage value
+ * @throws DeviceNotFoundException
+ */
+ PercentType getBatteryRemainingCapacity(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get battery name
+ *
+ * @param deviceIndex
+ * @throws DeviceNotFoundException
+ */
+ StringType getBatteryName(int deviceIndex) throws DeviceNotFoundException;
+
+ /**
+ * Get PID of process executing this code
+ *
+ * @return current process ID
+ */
+ int getCurrentProcessID();
+
+ /**
+ * Returns the name of the process
+ *
+ * @param pid - the PID of the process
+ * @throws DeviceNotFoundException - thrown if process with this PID can not be found
+ */
+ @Nullable
+ StringType getProcessName(int pid) throws DeviceNotFoundException;
+
+ /**
+ * Returns the CPU usage of the process
+ *
+ * @param pid - the PID of the process
+ * @return - percentage value, can be above 100% if process uses multiple cores
+ * @throws DeviceNotFoundException - thrown if process with this PID can not be found
+ */
+ @Nullable
+ DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException;
+
+ /**
+ * Returns the size of RAM memory only usage of the process
+ *
+ * @param pid - the PID of the process
+ * @return memory size
+ * @throws DeviceNotFoundException thrown if process with this PID can not be found
+ */
+ @Nullable
+ QuantityType<DataAmount> getProcessMemoryUsage(int pid) throws DeviceNotFoundException;
+
+ /**
+ * Returns the full path of the executing process.
+ *
+ * @param pid - the PID of the process
+ * @throws DeviceNotFoundException - thrown if process with this PID can not be found
+ */
+ @Nullable
+ StringType getProcessPath(int pid) throws DeviceNotFoundException;
+
+ /**
+ * Returns the number of threads in this process.
+ *
+ * @param pid - the PID of the process
+ * @throws DeviceNotFoundException - thrown if process with this PID can not be found
+ */
+ @Nullable
+ DecimalType getProcessThreads(int pid) throws DeviceNotFoundException;
+
+ /**
+ * Returns the number of network interfaces.
+ *
+ * @return network interface count
+ */
+ int getNetworkIFCount();
+
+ /**
+ * Returns the number of displays.
+ *
+ * @return display count
+ */
+ int getDisplayCount();
+
+ /**
+ * Returns the number of storages.
+ *
+ * @return storage count
+ */
+ int getFileOSStoreCount();
+
+ /**
+ * Returns the number of power sources/batteries.
+ *
+ * @return power source count
+ */
+ int getPowerSourceCount();
+
+ /**
+ * Returns the number of drives.
+ *
+ * @return drive count
+ */
+ int getDriveCount();
+
+ /**
+ * Returns the number of fans.
+ *
+ * @return fan count
+ */
+ int getFanCount();
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.internal.model;
-
-import javax.measure.quantity.ElectricPotential;
-import javax.measure.quantity.Frequency;
-import javax.measure.quantity.Temperature;
-import javax.measure.quantity.Time;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.library.dimension.DataAmount;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.StringType;
-
-/**
- * {@link SysteminfoInterface} defines the methods needed to provide this binding with the required system information.
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Wouter Born - Add null annotations
- * @author Mark Herwege - Add dynamic creation of extra channels
- * @author Mark Herwege - Use units of measure
- * @author Mark Herwege - Processor frequency channels
- */
-@NonNullByDefault
-public interface SysteminfoInterface {
-
- /**
- * Initialize logic for the Systeminfo implementation
- */
- void initializeSysteminfo();
-
- // Operating system info
- /**
- * Get the Family of the operating system /e.g. Windows, Unix,.../
- */
- StringType getOsFamily();
-
- /**
- * Get the manufacturer of the operating system
- */
- StringType getOsManufacturer();
-
- /**
- * Get the version of the operating system
- *
- * @return
- */
- StringType getOsVersion();
-
- // CPU info
- /**
- * Get the name of the CPU
- */
- StringType getCpuName();
-
- /**
- * Get description about the CPU e.g (model, family, vendor, serial number, identifier, architecture(32bit or
- * 64bit))
- */
- StringType getCpuDescription();
-
- /**
- * Get the number of logical CPUs/cores available for processing.
- */
- DecimalType getCpuLogicalCores();
-
- /**
- * Get the number of physical CPUs/cores available for processing.
- */
- DecimalType getCpuPhysicalCores();
-
- /**
- * Get the maximum CPU frequency of the processor.
- */
- @Nullable
- QuantityType<Frequency> getCpuMaxFreq();
-
- /**
- * Get the current CPU frequency of a logical processor.
- */
- @Nullable
- QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex);
-
- /**
- * Returns the system cpu load.
- *
- * @return the system cpu load between 0 and 100% or null, if no information is available
- */
- @Nullable
- PercentType getSystemCpuLoad();
-
- /**
- * Returns the system load average for the last minute.
- *
- * @return the load as a number of processes or null, if no information is available
- */
- @Nullable
- DecimalType getCpuLoad1();
-
- /**
- * Returns the system load average for the last 5 minutes.
- *
- * @return the load as number of processes or null, if no information is available
- */
- @Nullable
- DecimalType getCpuLoad5();
-
- /**
- * Returns the system load average for the last 15 minutes.
- *
- * @return the load as number of processes or null, if no information is available
- */
- @Nullable
- DecimalType getCpuLoad15();
-
- /**
- * Get the System uptime (time since boot).
- *
- * @return time since boot
- */
- QuantityType<Time> getCpuUptime();
-
- /**
- * Get the number of threads currently running
- *
- * @return number of threads
- */
- DecimalType getCpuThreads();
-
- // Memory info
- /**
- * Returns total size of memory
- *
- * @return memory size
- */
- QuantityType<DataAmount> getMemoryTotal();
-
- /**
- * Returns available size of memory
- *
- * @return memory size
- */
- QuantityType<DataAmount> getMemoryAvailable();
-
- /**
- * Returns used size of memory
- *
- * @return memory size
- */
- QuantityType<DataAmount> getMemoryUsed();
-
- /**
- * Percents of available memory on the machine
- *
- * @return percent of available memory or null, if no information is available
- */
- @Nullable
- PercentType getMemoryAvailablePercent();
-
- /**
- * Percents of used memory on the machine
- *
- * @return percent of used memory or null, if no information is available
- */
- @Nullable
- PercentType getMemoryUsedPercent();
-
- // Swap memory info
- /**
- * Returns total size of swap memory
- *
- * @return memory size or 0, if there is no swap memory
- */
- QuantityType<DataAmount> getSwapTotal();
-
- /**
- * Returns available size swap of memory
- *
- * @return memory size or 0, if no there is no swap memory
- */
- QuantityType<DataAmount> getSwapAvailable();
-
- /**
- * Returns used size of swap memory
- *
- * @return memory size or 0, if no there is no swap memory
- */
- QuantityType<DataAmount> getSwapUsed();
-
- /**
- * Percents of available swap memory on the machine
- *
- * @return percent of available memory or null, if no there is no swap memory
- */
- @Nullable
- PercentType getSwapAvailablePercent();
-
- /**
- * Percents of used swap memory on the machine
- *
- * @return percent of used memory or null, if no there is no swap memory
- */
- @Nullable
- PercentType getSwapUsedPercent();
-
- // Storage info
- /**
- * Returns the total space of the logical storage volume.
- *
- * @param deviceIndex - the index of the logical volume
- * @return storage size
- * @throws DeviceNotFoundException
- */
- QuantityType<DataAmount> getStorageTotal(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Returns the available storage space on the logical storage volume
- *
- * @param deviceIndex - the index of the logical volume
- * @return storage size
- * @throws DeviceNotFoundException
- */
- QuantityType<DataAmount> getStorageAvailable(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the used storage space on the logical storage volume
- *
- * @param deviceIndex - the index of the logical volume
- * @return storage size
- * @throws DeviceNotFoundException
- */
- QuantityType<DataAmount> getStorageUsed(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the percent of available storage on the logical volume
- *
- * @param deviceIndex - the index of the logical volume
- * @return percent of available storage or null
- * @throws DeviceNotFoundException
- */
- @Nullable
- PercentType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the percent of used storage on the logical volume
- *
- * @param deviceIndex - the index of the logical volume
- * @return percent of used storage or null
- * @throws DeviceNotFoundException
- */
- @Nullable
- PercentType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the name of the logical storage volume
- *
- * @throws DeviceNotFoundException
- */
- StringType getStorageName(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the type of the logical storage volume (e.g. NTFS, FAT32)
- *
- * @throws DeviceNotFoundException
- */
- StringType getStorageType(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the description of the logical storage volume
- *
- * @throws DeviceNotFoundException
- */
- StringType getStorageDescription(int deviceIndex) throws DeviceNotFoundException;
-
- // Hardware drive info
- /**
- * Gets the name of the physical storage drive
- *
- * @param deviceIndex - index of the storage drive
- * @throws DeviceNotFoundException
- */
- StringType getDriveName(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the model of the physical storage drive
- *
- * @param deviceIndex - index of the storage drive
- * @throws DeviceNotFoundException
- */
- StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the serial number of the physical storage drive
- *
- * @param deviceIndex - index of the storage drive
- * @throws DeviceNotFoundException
- */
- StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException;
-
- // Network info
- /**
- * Get the Host IP address of the network.
- *
- * @param networkIndex - the index of the network
- * @return 32-bit IPv4 address
- * @throws DeviceNotFoundException
- */
- StringType getNetworkIp(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Get the name of this network.
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- StringType getNetworkName(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * The description of the network. On some platforms, this is identical to the name.
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- StringType getNetworkDisplayName(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Gets the MAC Address of the network.
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Get number of packets received
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Get number of packets sent
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Get data sent for this network
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- QuantityType<DataAmount> getNetworkDataSent(int networkIndex) throws DeviceNotFoundException;
-
- /**
- * Get data received for this network
- *
- * @param networkIndex - the index of the network
- * @throws DeviceNotFoundException
- */
- QuantityType<DataAmount> getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException;
-
- // Display info
- /**
- * Get information about the display device as product number, manufacturer, serial number, width and height in cm";
- *
- * @param deviceIndex - the index of the display device
- * @throws DeviceNotFoundException
- */
- StringType getDisplayInformation(int deviceIndex) throws DeviceNotFoundException;
-
- // Sensors info
- /**
- * Get the information from the CPU temperature sensors.
- *
- * @return Temperature if available, null otherwise.
- */
- @Nullable
- QuantityType<Temperature> getSensorsCpuTemperature();
-
- /**
- * Get the information for the CPU voltage.
- *
- * @return Voltage if available, null otherwise.
- */
- @Nullable
- QuantityType<ElectricPotential> getSensorsCpuVoltage();
-
- /**
- * Get fan speed
- *
- * @param deviceIndex
- * @return Speed in rpm or null if unable to measure fan speed
- * @throws DeviceNotFoundException
- */
- @Nullable
- DecimalType getSensorsFanSpeed(int deviceIndex) throws DeviceNotFoundException;
-
- // Battery info
- /**
- * Get estimated time remaining for the power source.
- *
- * @param deviceIndex
- * @return duration remaining charge or null, if the time is estimated as unlimited
- * @throws DeviceNotFoundException
- */
- @Nullable
- QuantityType<Time> getBatteryRemainingTime(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Battery remaining capacity.
- *
- * @param deviceIndex
- * @return percentage value
- * @throws DeviceNotFoundException
- */
- PercentType getBatteryRemainingCapacity(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Get battery name
- *
- * @param deviceIndex
- * @throws DeviceNotFoundException
- */
- StringType getBatteryName(int deviceIndex) throws DeviceNotFoundException;
-
- /**
- * Get PID of process executing this code
- *
- * @return current process ID
- */
- int getCurrentProcessID();
-
- /**
- * Returns the name of the process
- *
- * @param pid - the PID of the process
- * @throws DeviceNotFoundException - thrown if process with this PID can not be found
- */
- @Nullable
- StringType getProcessName(int pid) throws DeviceNotFoundException;
-
- /**
- * Returns the CPU usage of the process
- *
- * @param pid - the PID of the process
- * @return - percentage value, can be above 100% if process uses multiple cores
- * @throws DeviceNotFoundException - thrown if process with this PID can not be found
- */
- @Nullable
- DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException;
-
- /**
- * Returns the size of RAM memory only usage of the process
- *
- * @param pid - the PID of the process
- * @return memory size
- * @throws DeviceNotFoundException thrown if process with this PID can not be found
- */
- @Nullable
- QuantityType<DataAmount> getProcessMemoryUsage(int pid) throws DeviceNotFoundException;
-
- /**
- * Returns the full path of the executing process.
- *
- * @param pid - the PID of the process
- * @throws DeviceNotFoundException - thrown if process with this PID can not be found
- */
- @Nullable
- StringType getProcessPath(int pid) throws DeviceNotFoundException;
-
- /**
- * Returns the number of threads in this process.
- *
- * @param pid - the PID of the process
- * @throws DeviceNotFoundException - thrown if process with this PID can not be found
- */
- @Nullable
- DecimalType getProcessThreads(int pid) throws DeviceNotFoundException;
-
- /**
- * Returns the number of network interfaces.
- *
- * @return network interface count
- */
- int getNetworkIFCount();
-
- /**
- * Returns the number of displays.
- *
- * @return display count
- */
- int getDisplayCount();
-
- /**
- * Returns the number of storages.
- *
- * @return storage count
- */
- int getFileOSStoreCount();
-
- /**
- * Returns the number of power sources/batteries.
- *
- * @return power source count
- */
- int getPowerSourceCount();
-
- /**
- * Returns the number of drives.
- *
- * @return drive count
- */
- int getDriveCount();
-
- /**
- * Returns the number of fans.
- *
- * @return fan count
- */
- int getFanCount();
-}
<artifactId>org.openhab.binding.systeminfo.tests</artifactId>
- <name>openHAB Add-ons :: Integration Tests :: Systeminfo Binding Tests</name>
+ <name>openHAB Add-ons :: Integration Tests :: SystemInfo Binding Tests</name>
<dependencies>
<dependency>
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.systeminfo.test;
+
+import static java.lang.Thread.sleep;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.Mockito.*;
+
+import java.math.BigDecimal;
+import java.net.UnknownHostException;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.measure.quantity.ElectricPotential;
+import javax.measure.quantity.Frequency;
+import javax.measure.quantity.Temperature;
+import javax.measure.quantity.Time;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.openhab.binding.systeminfo.internal.SystemInfoBindingConstants;
+import org.openhab.binding.systeminfo.internal.SystemInfoHandlerFactory;
+import org.openhab.binding.systeminfo.internal.discovery.SystemInfoDiscoveryService;
+import org.openhab.binding.systeminfo.internal.handler.SystemInfoHandler;
+import org.openhab.binding.systeminfo.internal.model.DeviceNotFoundException;
+import org.openhab.binding.systeminfo.internal.model.OSHISystemInfo;
+import org.openhab.binding.systeminfo.internal.model.SystemInfoInterface;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.config.discovery.inbox.Inbox;
+import org.openhab.core.config.discovery.inbox.InboxPredicates;
+import org.openhab.core.i18n.UnitProvider;
+import org.openhab.core.items.GenericItem;
+import org.openhab.core.items.ItemNotFoundException;
+import org.openhab.core.items.ItemRegistry;
+import org.openhab.core.library.dimension.DataAmount;
+import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.library.items.StringItem;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.test.java.JavaOSGiTest;
+import org.openhab.core.test.storage.VolatileStorageService;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ManagedThingProvider;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingProvider;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.link.ItemChannelLink;
+import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
+import org.openhab.core.thing.type.ChannelKind;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * OSGi tests for the {@link SystemInfoHandler}
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Lyubomir Papazov - Created a mock systeminfo object. This way, access to the user's OS will not be required,
+ * but mock data will be used instead, avoiding potential errors from the OS queries.
+ * @author Wouter Born - Migrate Groovy to Java tests
+ * @author Mark Herwege - Processor frequency channels
+ */
+@NonNullByDefault
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+public class SystemInfoOSGiTest extends JavaOSGiTest {
+
+ private static final String DEFAULT_TEST_THING_NAME = "work";
+ private static final String DEFAULT_TEST_ITEM_NAME = "test";
+ private static final String DEFAULT_CHANNEL_TEST_PRIORITY = "High";
+ private static final int DEFAULT_CHANNEL_PID = -1;
+ private static final String DEFAULT_TEST_CHANNEL_ID = SystemInfoBindingConstants.CHANNEL_CPU_LOAD;
+ private static final int DEFAULT_DEVICE_INDEX = 0;
+
+ /**
+ * Refresh time in seconds for tasks with priority High.
+ * Default value for the parameter interval_high in the thing configuration
+ */
+ private static final int DEFAULT_TEST_INTERVAL_HIGH = 1;
+
+ /**
+ * Refresh time in seconds for tasks with priority Medium.
+ */
+ private static final int DEFAULT_TEST_INTERVAL_MEDIUM = 3;
+
+ private @Nullable Thing systeminfoThing;
+ private @Nullable GenericItem testItem;
+
+ private @Mock @NonNullByDefault({}) OSHISystemInfo mockedSystemInfo;
+ private @NonNullByDefault({}) SystemInfoHandlerFactory systeminfoHandlerFactory;
+ private @NonNullByDefault({}) ThingRegistry thingRegistry;
+ private @NonNullByDefault({}) ItemRegistry itemRegistry;
+ private @NonNullByDefault({}) ManagedThingProvider managedThingProvider;
+ private @NonNullByDefault({}) ManagedItemChannelLinkProvider itemChannelLinkProvider;
+ private @NonNullByDefault({}) UnitProvider unitProvider;
+ private @NonNullByDefault({}) VolatileStorageService volatileStorageService;
+
+ @BeforeEach
+ public void setUp() {
+ volatileStorageService = new VolatileStorageService();
+ registerService(volatileStorageService);
+
+ // Preparing the mock with OS properties, that are used in the initialize method of SystemInfoHandler
+ // Make this lenient because the assertInvalidThingConfigurationValuesAreHandled test does not require them
+ lenient().when(mockedSystemInfo.getCpuLogicalCores()).thenReturn(new DecimalType(1));
+ lenient().when(mockedSystemInfo.getCpuPhysicalCores()).thenReturn(new DecimalType(1));
+ lenient().when(mockedSystemInfo.getOsFamily()).thenReturn(new StringType("Mock OS"));
+ lenient().when(mockedSystemInfo.getOsManufacturer()).thenReturn(new StringType("Mock OS Manufacturer"));
+ lenient().when(mockedSystemInfo.getOsVersion()).thenReturn(new StringType("Mock Os Version"));
+ // Following mock method returns will make sure the thing does not get recreated with extra channels
+ lenient().when(mockedSystemInfo.getNetworkIFCount()).thenReturn(1);
+ lenient().when(mockedSystemInfo.getDisplayCount()).thenReturn(1);
+ lenient().when(mockedSystemInfo.getFileOSStoreCount()).thenReturn(1);
+ lenient().when(mockedSystemInfo.getPowerSourceCount()).thenReturn(1);
+ lenient().when(mockedSystemInfo.getDriveCount()).thenReturn(1);
+ lenient().when(mockedSystemInfo.getFanCount()).thenReturn(1);
+
+ registerService(mockedSystemInfo);
+
+ waitForAssert(() -> {
+ systeminfoHandlerFactory = getService(ThingHandlerFactory.class, SystemInfoHandlerFactory.class);
+ assertThat(systeminfoHandlerFactory, is(notNullValue()));
+ });
+
+ if (systeminfoHandlerFactory != null) {
+ // Unbind oshiSystemInfo service and bind the mock service to make the systeminfo binding tests independent
+ // of the external OSHI library
+ SystemInfoInterface oshiSystemInfo = getService(SystemInfoInterface.class);
+ if (oshiSystemInfo != null) {
+ systeminfoHandlerFactory.unbindSystemInfo(oshiSystemInfo);
+ }
+ systeminfoHandlerFactory.bindSystemInfo(mockedSystemInfo);
+ }
+
+ waitForAssert(() -> {
+ thingRegistry = getService(ThingRegistry.class);
+ assertThat(thingRegistry, is(notNullValue()));
+ });
+
+ waitForAssert(() -> {
+ itemRegistry = getService(ItemRegistry.class);
+ assertThat(itemRegistry, is(notNullValue()));
+ });
+
+ waitForAssert(() -> {
+ managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
+ assertThat(managedThingProvider, is(notNullValue()));
+ });
+
+ waitForAssert(() -> {
+ itemChannelLinkProvider = getService(ManagedItemChannelLinkProvider.class);
+ assertThat(itemChannelLinkProvider, is(notNullValue()));
+ });
+
+ waitForAssert(() -> {
+ unitProvider = getService(UnitProvider.class);
+ assertThat(unitProvider, is(notNullValue()));
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ Thing thing = systeminfoThing;
+ if (thing != null) {
+ // Remove the systeminfo thing. The handler will also be disposed automatically
+ Thing removedThing = thingRegistry.forceRemove(thing.getUID());
+ assertThat("The systeminfo thing cannot be deleted", removedThing, is(notNullValue()));
+ waitForAssert(() -> {
+ ThingHandler systemInfoHandler = thing.getHandler();
+ assertThat(systemInfoHandler, is(nullValue()));
+ });
+ managedThingProvider.remove(thing.getUID());
+ }
+
+ if (testItem != null) {
+ itemRegistry.remove(DEFAULT_TEST_ITEM_NAME);
+ }
+
+ unregisterService(mockedSystemInfo);
+ unregisterService(volatileStorageService);
+ }
+
+ private void initializeThingWithChannelAndPID(String channelID, String acceptedItemType, int pid) {
+ Configuration thingConfig = new Configuration();
+ thingConfig.put(SystemInfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
+ thingConfig.put(SystemInfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
+ String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
+
+ initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
+ }
+
+ private void initializeThingWithChannelAndPriority(String channelID, String acceptedItemType, String priority) {
+ Configuration thingConfig = new Configuration();
+ thingConfig.put(SystemInfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
+ thingConfig.put(SystemInfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
+ int pid = DEFAULT_CHANNEL_PID;
+
+ initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
+ }
+
+ private void initializeThingWithConfiguration(Configuration config) {
+ String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
+ String channelID = DEFAULT_TEST_CHANNEL_ID;
+ String acceptedItemType = "String";
+ int pid = DEFAULT_CHANNEL_PID;
+
+ initializeThing(config, channelID, acceptedItemType, priority, pid);
+ }
+
+ private void initializeThingWithChannel(String channelID, String acceptedItemType) {
+ Configuration thingConfig = new Configuration();
+ thingConfig.put(SystemInfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
+ thingConfig.put(SystemInfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
+ new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
+
+ String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
+ int pid = DEFAULT_CHANNEL_PID;
+ initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
+ }
+
+ private void initializeThing(Configuration thingConfiguration, String channelID, String acceptedItemType,
+ String priority, int pid) {
+ ThingTypeUID thingTypeUID = SystemInfoBindingConstants.THING_TYPE_COMPUTER;
+ ThingUID thingUID = new ThingUID(thingTypeUID, DEFAULT_TEST_THING_NAME);
+
+ ChannelUID channelUID = new ChannelUID(thingUID, channelID);
+ String channelTypeId = channelUID.getIdWithoutGroup();
+ if ("load1".equals(channelTypeId) || "load5".equals(channelTypeId) || "load15".equals(channelTypeId)) {
+ channelTypeId = "loadAverage";
+ }
+ ChannelTypeUID channelTypeUID = new ChannelTypeUID(SystemInfoBindingConstants.BINDING_ID, channelTypeId);
+ Configuration channelConfig = new Configuration();
+ channelConfig.put("priority", priority);
+ channelConfig.put("pid", new BigDecimal(pid));
+ Channel channel = ChannelBuilder.create(channelUID, acceptedItemType).withType(channelTypeUID)
+ .withKind(ChannelKind.STATE).withConfiguration(channelConfig).build();
+
+ ThingBuilder thingBuilder = ThingBuilder.create(thingTypeUID, thingUID).withConfiguration(thingConfiguration)
+ .withChannel(channel);
+ // Make sure the thingTypeVersion matches the highest version in the update instructions of the binding to avoid
+ // new channels being added and the thing not initializing
+ thingBuilder = thingBuilder.withProperties(Map.of("thingTypeVersion", "1"));
+ Thing thing = thingBuilder.build();
+ systeminfoThing = thing;
+
+ managedThingProvider.add(thing);
+
+ waitForAssert(() -> {
+ SystemInfoHandler handler = (SystemInfoHandler) thing.getHandler();
+ assertThat(handler, is(notNullValue()));
+ });
+
+ waitForAssert(() -> {
+ assertThat("Thing is not initialized, before an Item is created", thing.getStatus(),
+ anyOf(equalTo(ThingStatus.OFFLINE), equalTo(ThingStatus.ONLINE)));
+ });
+
+ intializeItem(channelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
+ }
+
+ private void assertItemState(String acceptedItemType, String itemName, String priority, State expectedState) {
+ Thing thing = systeminfoThing;
+ if (thing == null) {
+ throw new AssertionError("Thing is null");
+ }
+ waitForAssert(() -> {
+ ThingStatusDetail thingStatusDetail = thing.getStatusInfo().getStatusDetail();
+ String description = thing.getStatusInfo().getDescription();
+ assertThat("Thing status detail is " + thingStatusDetail + " with description " + description,
+ thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
+ });
+ // The binding starts all refresh tasks in SystemInfoHandler.scheduleUpdates() after this delay !
+ try {
+ sleep(SystemInfoHandler.WAIT_TIME_CHANNEL_ITEM_LINK_INIT * 1000);
+ } catch (InterruptedException e) {
+ throw new AssertionError("Interrupted while sleeping");
+ }
+
+ GenericItem item;
+ try {
+ item = (GenericItem) itemRegistry.getItem(itemName);
+ } catch (ItemNotFoundException e) {
+ throw new AssertionError("Item not found in registry");
+ }
+
+ int waitTime;
+ if ("High".equals(priority)) {
+ waitTime = DEFAULT_TEST_INTERVAL_HIGH * 1000;
+ } else if ("Medium".equals(priority)) {
+ waitTime = DEFAULT_TEST_INTERVAL_MEDIUM * 1000;
+ } else {
+ waitTime = 100;
+ }
+
+ waitForAssert(() -> {
+ State itemState = item.getState();
+ assertThat(itemState, is(equalTo(expectedState)));
+ }, waitTime, DFL_SLEEP_TIME);
+ }
+
+ private void intializeItem(ChannelUID channelUID, String itemName, String acceptedItemType) {
+ GenericItem item = null;
+ if (acceptedItemType.startsWith("Number")) {
+ item = new NumberItem(acceptedItemType, itemName, unitProvider);
+ } else if ("String".equals(acceptedItemType)) {
+ item = new StringItem(itemName);
+ }
+ if (item == null) {
+ throw new AssertionError("Item is null");
+ }
+ itemRegistry.add(item);
+ testItem = item;
+
+ itemChannelLinkProvider.add(new ItemChannelLink(itemName, channelUID));
+ }
+
+ @Test
+ public void assertInvalidThingConfigurationValuesAreHandled() {
+ Configuration configuration = new Configuration();
+
+ // invalid value - must be positive
+ int refreshIntervalHigh = -5;
+ configuration.put(SystemInfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME, new BigDecimal(refreshIntervalHigh));
+
+ int refreshIntervalMedium = 3;
+ configuration.put(SystemInfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
+ new BigDecimal(refreshIntervalMedium));
+ initializeThingWithConfiguration(configuration);
+
+ testInvalidConfiguration();
+ }
+
+ private void testInvalidConfiguration() {
+ waitForAssert(() -> {
+ Thing thing = systeminfoThing;
+ if (thing != null) {
+ assertThat("Invalid configuration is used !", thing.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
+ assertThat(thing.getStatusInfo().getStatusDetail(),
+ is(equalTo(ThingStatusDetail.HANDLER_INITIALIZING_ERROR)));
+ assertThat(thing.getStatusInfo().getDescription(), is(equalTo("@text/offline.cannot-initialize")));
+ }
+ });
+ }
+
+ @Test
+ public void assertMediumPriorityChannelIsUpdated() {
+ String channnelID = DEFAULT_TEST_CHANNEL_ID;
+ String acceptedItemType = "Number";
+ String priority = "Medium";
+
+ initializeThingWithChannelAndPriority(channnelID, acceptedItemType, priority);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, priority, UnDefType.UNDEF);
+ }
+
+ @Test
+ public void assertStateOfSecondDeviceIsUpdated() {
+ // This test assumes that at least 2 network interfaces are present on the test platform
+ int deviceIndex = 1;
+ String channnelID = "network" + deviceIndex + "#mac";
+ String acceptedItemType = "String";
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, UnDefType.UNDEF);
+ }
+
+ @Test
+ public void assertChannelCpuMaxFreq() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_MAXFREQ;
+ String acceptedItemType = "Number:Frequency";
+
+ QuantityType<Frequency> mockedCpuMaxFreqValue = new QuantityType<>(2500, Units.HERTZ);
+ when(mockedSystemInfo.getCpuMaxFreq()).thenReturn(mockedCpuMaxFreqValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuMaxFreqValue);
+ }
+
+ @Test
+ public void assertChannelCpuFreq() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_FREQ;
+ String acceptedItemType = "Number:Frequency";
+
+ QuantityType<Frequency> mockedCpuFreqValue = new QuantityType<>(2500, Units.HERTZ);
+ when(mockedSystemInfo.getCpuFreq(0)).thenReturn(mockedCpuFreqValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuFreqValue);
+ }
+
+ @Test
+ public void assertChannelCpuLoadIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_LOAD;
+ String acceptedItemType = "Number";
+
+ PercentType mockedCpuLoadValue = new PercentType(9);
+ when(mockedSystemInfo.getSystemCpuLoad()).thenReturn(mockedCpuLoadValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoadValue);
+ }
+
+ @Test
+ public void assertChannelCpuLoad1IsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_LOAD_1;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedCpuLoad1Value = new DecimalType(1.1);
+ when(mockedSystemInfo.getCpuLoad1()).thenReturn(mockedCpuLoad1Value);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad1Value);
+ }
+
+ @Test
+ public void assertChannelCpuLoad5IsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_LOAD_5;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedCpuLoad5Value = new DecimalType(5.5);
+ when(mockedSystemInfo.getCpuLoad5()).thenReturn(mockedCpuLoad5Value);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad5Value);
+ }
+
+ @Test
+ public void assertChannelCpuLoad15IsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_LOAD_15;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedCpuLoad15Value = new DecimalType(15.15);
+ when(mockedSystemInfo.getCpuLoad15()).thenReturn(mockedCpuLoad15Value);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad15Value);
+ }
+
+ @Test
+ public void assertChannelCpuThreadsIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_THREADS;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedCpuThreadsValue = new DecimalType(16);
+ when(mockedSystemInfo.getCpuThreads()).thenReturn(mockedCpuThreadsValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuThreadsValue);
+ }
+
+ @Test
+ public void assertChannelCpuUptimeIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_UPTIME;
+ String acceptedItemType = "Number:Time";
+
+ QuantityType<Time> mockedCpuUptimeValue = new QuantityType<>(100, Units.MINUTE);
+ when(mockedSystemInfo.getCpuUptime()).thenReturn(mockedCpuUptimeValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuUptimeValue);
+ }
+
+ @Test
+ public void assertChannelCpuDescriptionIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_DESCRIPTION;
+ String acceptedItemType = "String";
+
+ StringType mockedCpuDescriptionValue = new StringType("Mocked Cpu Descr");
+ when(mockedSystemInfo.getCpuDescription()).thenReturn(mockedCpuDescriptionValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedCpuDescriptionValue);
+ }
+
+ @Test
+ public void assertChannelCpuNameIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_CPU_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedCpuNameValue = new StringType("Mocked Cpu Name");
+ when(mockedSystemInfo.getCpuName()).thenReturn(mockedCpuNameValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuNameValue);
+ }
+
+ @Test
+ public void assertChannelMemoryAvailableIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_MEMORY_AVAILABLE;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedMemoryAvailableValue = new QuantityType<>(1000, Units.MEBIBYTE);
+ when(mockedSystemInfo.getMemoryAvailable()).thenReturn(mockedMemoryAvailableValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedMemoryAvailableValue);
+ }
+
+ @Test
+ public void assertChannelMemoryUsedIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_MEMORY_USED;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedMemoryUsedValue = new QuantityType<>(24, Units.MEBIBYTE);
+ when(mockedSystemInfo.getMemoryUsed()).thenReturn(mockedMemoryUsedValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedMemoryUsedValue);
+ }
+
+ @Test
+ public void assertChannelMemoryTotalIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_MEMORY_TOTAL;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedMemoryTotalValue = new QuantityType<>(1024, Units.MEBIBYTE);
+ when(mockedSystemInfo.getMemoryTotal()).thenReturn(mockedMemoryTotalValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedMemoryTotalValue);
+ }
+
+ @Test
+ public void assertChannelMemoryAvailablePercentIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_MEMORY_AVAILABLE_PERCENT;
+ String acceptedItemType = "Number";
+
+ PercentType mockedMemoryAvailablePercentValue = new PercentType(97);
+ when(mockedSystemInfo.getMemoryAvailablePercent()).thenReturn(mockedMemoryAvailablePercentValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedMemoryAvailablePercentValue);
+ }
+
+ @Test
+ public void assertChannelSwapAvailableIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SWAP_AVAILABLE;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedSwapAvailableValue = new QuantityType<>(482, Units.MEBIBYTE);
+ when(mockedSystemInfo.getSwapAvailable()).thenReturn(mockedSwapAvailableValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedSwapAvailableValue);
+ }
+
+ @Test
+ public void assertChannelSwapUsedIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SWAP_USED;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedSwapUsedValue = new QuantityType<>(30, Units.MEBIBYTE);
+ when(mockedSystemInfo.getSwapUsed()).thenReturn(mockedSwapUsedValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedSwapUsedValue);
+ }
+
+ @Test
+ public void assertChannelSwapTotalIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SWAP_TOTAL;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedSwapTotalValue = new QuantityType<>(512, Units.MEBIBYTE);
+ when(mockedSystemInfo.getSwapTotal()).thenReturn(mockedSwapTotalValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedSwapTotalValue);
+ }
+
+ @Test
+ public void assertChannelSwapAvailablePercentIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SWAP_AVAILABLE_PERCENT;
+ String acceptedItemType = "Number";
+
+ PercentType mockedSwapAvailablePercentValue = new PercentType(94);
+ when(mockedSystemInfo.getSwapAvailablePercent()).thenReturn(mockedSwapAvailablePercentValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedSwapAvailablePercentValue);
+ }
+
+ @Test
+ public void assertChannelStorageNameIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedStorageName = new StringType("Mocked Storage Name");
+ when(mockedSystemInfo.getStorageName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageName);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedStorageName);
+ }
+
+ @Test
+ public void assertChannelStorageTypeIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_TYPE;
+ String acceptedItemType = "String";
+
+ StringType mockedStorageType = new StringType("Mocked Storage Type");
+ when(mockedSystemInfo.getStorageType(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageType);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedStorageType);
+ }
+
+ @Test
+ public void assertChannelStorageDescriptionIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_DESCRIPTION;
+ String acceptedItemType = "String";
+
+ StringType mockedStorageDescription = new StringType("Mocked Storage Description");
+ when(mockedSystemInfo.getStorageDescription(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageDescription);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedStorageDescription);
+ }
+
+ @Test
+ public void assertChannelStorageAvailableIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_AVAILABLE;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedStorageAvailableValue = new QuantityType<>(2000, Units.MEBIBYTE);
+ when(mockedSystemInfo.getStorageAvailable(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageAvailableValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedStorageAvailableValue);
+ }
+
+ @Test
+ public void assertChannelStorageUsedIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_USED;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedStorageUsedValue = new QuantityType<>(500, Units.MEBIBYTE);
+ when(mockedSystemInfo.getStorageUsed(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageUsedValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedStorageUsedValue);
+ }
+
+ @Test
+ public void assertChannelStorageTotalIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_TOTAL;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedStorageTotalValue = new QuantityType<>(2500, Units.MEBIBYTE);
+ when(mockedSystemInfo.getStorageTotal(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageTotalValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedStorageTotalValue);
+ }
+
+ @Test
+ public void assertChannelStorageAvailablePercentIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_STORAGE_AVAILABLE_PERCENT;
+ String acceptedItemType = "Number";
+
+ PercentType mockedStorageAvailablePercent = new PercentType(20);
+ when(mockedSystemInfo.getStorageAvailablePercent(DEFAULT_DEVICE_INDEX))
+ .thenReturn(mockedStorageAvailablePercent);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedStorageAvailablePercent);
+ }
+
+ @Test
+ public void assertChannelDriveNameIsUpdated() throws DeviceNotFoundException {
+ String channelID = SystemInfoBindingConstants.CHANNEL_DRIVE_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedDriveNameValue = new StringType("Mocked Drive Name");
+ when(mockedSystemInfo.getDriveName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveNameValue);
+
+ initializeThingWithChannel(channelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDriveNameValue);
+ }
+
+ @Test
+ public void assertChannelDriveModelIsUpdated() throws DeviceNotFoundException {
+ String channelID = SystemInfoBindingConstants.CHANNEL_DRIVE_MODEL;
+ String acceptedItemType = "String";
+
+ StringType mockedDriveModelValue = new StringType("Mocked Drive Model");
+ when(mockedSystemInfo.getDriveModel(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveModelValue);
+
+ initializeThingWithChannel(channelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDriveModelValue);
+ }
+
+ @Test
+ public void assertChannelDriveSerialIsUpdated() throws DeviceNotFoundException {
+ String channelID = SystemInfoBindingConstants.CHANNEL_DRIVE_SERIAL;
+ String acceptedItemType = "String";
+
+ StringType mockedDriveSerialNumber = new StringType("Mocked Drive Serial Number");
+ when(mockedSystemInfo.getDriveSerialNumber(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveSerialNumber);
+
+ initializeThingWithChannel(channelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedDriveSerialNumber);
+ }
+
+ // Re-enable this previously disabled test, as it is not relying on hardware anymore, but a mocked object
+ // There is a bug opened for this issue - https://github.com/dblock/oshi/issues/185
+ @Test
+ public void assertChannelSensorsCpuTempIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SENSORS_CPU_TEMPERATURE;
+ String acceptedItemType = "Number:Temperature";
+
+ QuantityType<Temperature> mockedSensorsCpuTemperatureValue = new QuantityType<>(60, SIUnits.CELSIUS);
+ when(mockedSystemInfo.getSensorsCpuTemperature()).thenReturn(mockedSensorsCpuTemperatureValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedSensorsCpuTemperatureValue);
+ }
+
+ @Test
+ public void assertChannelSensorsCpuVoltageIsUpdated() {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SENOSRS_CPU_VOLTAGE;
+ String acceptedItemType = "Number:ElectricPotential";
+
+ QuantityType<ElectricPotential> mockedSensorsCpuVoltageValue = new QuantityType<>(1000, Units.VOLT);
+ when(mockedSystemInfo.getSensorsCpuVoltage()).thenReturn(mockedSensorsCpuVoltageValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedSensorsCpuVoltageValue);
+ }
+
+ @Test
+ public void assertChannelSensorsFanSpeedIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_SENSORS_FAN_SPEED;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedSensorsCpuFanSpeedValue = new DecimalType(180);
+ when(mockedSystemInfo.getSensorsFanSpeed(DEFAULT_DEVICE_INDEX)).thenReturn(mockedSensorsCpuFanSpeedValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedSensorsCpuFanSpeedValue);
+ }
+
+ @Test
+ public void assertChannelBatteryNameIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_BATTERY_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedBatteryName = new StringType("Mocked Battery Name");
+ when(mockedSystemInfo.getBatteryName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedBatteryName);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedBatteryName);
+ }
+
+ @Test
+ public void assertChannelBatteryRemainingCapacityIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_BATTERY_REMAINING_CAPACITY;
+ String acceptedItemType = "Number";
+
+ PercentType mockedBatteryRemainingCapacity = new PercentType(20);
+ when(mockedSystemInfo.getBatteryRemainingCapacity(DEFAULT_DEVICE_INDEX))
+ .thenReturn(mockedBatteryRemainingCapacity);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedBatteryRemainingCapacity);
+ }
+
+ @Test
+ public void assertChannelBatteryRemainingTimeIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_BATTERY_REMAINING_TIME;
+ String acceptedItemType = "Number:Time";
+
+ QuantityType<Time> mockedBatteryRemainingTime = new QuantityType<>(3600, Units.MINUTE);
+ when(mockedSystemInfo.getBatteryRemainingTime(DEFAULT_DEVICE_INDEX)).thenReturn(mockedBatteryRemainingTime);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedBatteryRemainingTime);
+ }
+
+ @Test
+ public void assertChannelDisplayInformationIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_DISPLAY_INFORMATION;
+ String acceptedItemType = "String";
+
+ StringType mockedDisplayInfo = new StringType("Mocked Display Information");
+ when(mockedSystemInfo.getDisplayInformation(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDisplayInfo);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDisplayInfo);
+ }
+
+ @Test
+ public void assertChannelNetworkIpIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_IP;
+ String acceptedItemType = "String";
+
+ StringType mockedNetworkIp = new StringType("192.168.1.0");
+ when(mockedSystemInfo.getNetworkIp(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkIp);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkIp);
+ }
+
+ @Test
+ public void assertChannelNetworkMacIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_MAC;
+ String acceptedItemType = "String";
+
+ StringType mockedNetworkMacValue = new StringType("AB-10-11-12-13-14");
+ when(mockedSystemInfo.getNetworkMac(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkMacValue);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkMacValue);
+ }
+
+ @Test
+ public void assertChannelNetworkDataSentIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_DATA_SENT;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedNetworkDataSent = new QuantityType<>(1000, Units.MEBIBYTE);
+ when(mockedSystemInfo.getNetworkDataSent(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkDataSent);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkDataSent);
+ }
+
+ @Test
+ public void assertChannelNetworkDataReceivedIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_DATA_RECEIVED;
+ String acceptedItemType = "Number:DataAmount";
+
+ QuantityType<DataAmount> mockedNetworkDataReceiveed = new QuantityType<>(800, Units.MEBIBYTE);
+ when(mockedSystemInfo.getNetworkDataReceived(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkDataReceiveed);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedNetworkDataReceiveed);
+ }
+
+ @Test
+ public void assertChannelNetworkPacketsSentIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_PACKETS_SENT;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedNetworkPacketsSent = new DecimalType(50);
+ when(mockedSystemInfo.getNetworkPacketsSent(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkPacketsSent);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedNetworkPacketsSent);
+ }
+
+ @Test
+ public void assertChannelNetworkPacketsReceivedIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_PACKETS_RECEIVED;
+ String acceptedItemType = "Number";
+
+ DecimalType mockedNetworkPacketsReceived = new DecimalType(48);
+ when(mockedSystemInfo.getNetworkPacketsReceived(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkPacketsReceived);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedNetworkPacketsReceived);
+ }
+
+ @Test
+ public void assertChannelNetworkNetworkNameIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedNetworkName = new StringType("MockN-AQ34");
+ when(mockedSystemInfo.getNetworkName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkName);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkName);
+ }
+
+ @Test
+ public void assertChannelNetworkNetworkDisplayNameIsUpdated() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_NETWORK_ADAPTER_NAME;
+ String acceptedItemType = "String";
+
+ StringType mockedNetworkAdapterName = new StringType("Mocked Network Adapter Name");
+ when(mockedSystemInfo.getNetworkDisplayName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkAdapterName);
+
+ initializeThingWithChannel(channnelID, acceptedItemType);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedNetworkAdapterName);
+ }
+
+ class SystemInfoDiscoveryServiceMock extends SystemInfoDiscoveryService {
+ String hostname;
+
+ SystemInfoDiscoveryServiceMock(String hostname) {
+ super();
+ this.hostname = hostname;
+ }
+
+ @Override
+ protected String getHostName() throws UnknownHostException {
+ if ("unresolved".equals(hostname)) {
+ throw new UnknownHostException();
+ }
+ return hostname;
+ }
+
+ @Override
+ public void startScan() {
+ super.startScan();
+ }
+ }
+
+ @Test
+ public void testDiscoveryWithInvalidHostname() {
+ String hostname = "Hilo.fritz.box";
+ String expectedHostname = "Hilo_fritz_box";
+
+ testDiscoveryService(expectedHostname, hostname);
+ }
+
+ @Test
+ public void testDiscoveryWithValidHostname() {
+ String hostname = "MyComputer";
+ String expectedHostname = "MyComputer";
+
+ testDiscoveryService(expectedHostname, hostname);
+ }
+
+ @Test
+ public void testDiscoveryWithUnresolvedHostname() {
+ String hostname = "unresolved";
+ String expectedHostname = SystemInfoDiscoveryService.DEFAULT_THING_ID;
+
+ testDiscoveryService(expectedHostname, hostname);
+ }
+
+ @Test
+ public void testDiscoveryWithEmptyHostnameString() {
+ String hostname = "";
+ String expectedHostname = SystemInfoDiscoveryService.DEFAULT_THING_ID;
+
+ testDiscoveryService(expectedHostname, hostname);
+ }
+
+ private void testDiscoveryService(String expectedHostname, String hostname) {
+ SystemInfoDiscoveryService discoveryService = getService(DiscoveryService.class,
+ SystemInfoDiscoveryService.class);
+ waitForAssert(() -> {
+ assertThat(discoveryService, is(notNullValue()));
+ });
+ SystemInfoDiscoveryServiceMock discoveryServiceMock = new SystemInfoDiscoveryServiceMock(hostname);
+ if (discoveryService != null) {
+ unregisterService(DiscoveryService.class);
+ }
+ registerService(discoveryServiceMock, DiscoveryService.class.getName(), new Hashtable<>());
+
+ ThingTypeUID computerType = SystemInfoBindingConstants.THING_TYPE_COMPUTER;
+ ThingUID computerUID = new ThingUID(computerType, expectedHostname);
+
+ discoveryServiceMock.startScan();
+
+ Inbox inbox = getService(Inbox.class);
+ waitForAssert(() -> {
+ assertThat(inbox, is(notNullValue()));
+ });
+
+ if (inbox == null) {
+ return;
+ }
+
+ waitForAssert(() -> {
+ List<DiscoveryResult> results = inbox.stream().filter(InboxPredicates.forThingUID(computerUID)).toList();
+ assertFalse(results.isEmpty(), "No Thing with UID " + computerUID.getAsString() + " in inbox");
+ });
+
+ inbox.approve(computerUID, SystemInfoDiscoveryService.DEFAULT_THING_LABEL, null);
+
+ waitForAssert(() -> {
+ systeminfoThing = thingRegistry.get(computerUID);
+ assertThat(systeminfoThing, is(notNullValue()));
+ });
+
+ Thing thing = systeminfoThing;
+ if (thing == null) {
+ return;
+ }
+
+ waitForAssert(() -> {
+ assertThat("Thing is not initialized.", thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
+ });
+ }
+
+ @Test
+ public void assertChannelProcessThreadsIsUpdatedWithPIDse() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_PROCESS_THREADS;
+ String acceptedItemType = "Number";
+ // The pid of the System idle process in Windows
+ int pid = 0;
+
+ DecimalType mockedProcessThreadsCount = new DecimalType(4);
+ when(mockedSystemInfo.getProcessThreads(pid)).thenReturn(mockedProcessThreadsCount);
+
+ initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
+ mockedProcessThreadsCount);
+ }
+
+ @Test
+ public void assertChannelProcessPathIsUpdatedWithPIDset() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_PROCESS_PATH;
+ String acceptedItemType = "String";
+ // The pid of the System idle process in Windows
+ int pid = 0;
+
+ StringType mockedProcessPath = new StringType("C:\\Users\\MockedUser\\Process");
+ when(mockedSystemInfo.getProcessPath(pid)).thenReturn(mockedProcessPath);
+
+ initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessPath);
+ }
+
+ @Test
+ public void assertChannelProcessNameIsUpdatedWithPIDset() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_PROCESS_NAME;
+ String acceptedItemType = "String";
+ // The pid of the System idle process in Windows
+ int pid = 0;
+
+ StringType mockedProcessName = new StringType("MockedProcess.exe");
+ when(mockedSystemInfo.getProcessName(pid)).thenReturn(mockedProcessName);
+
+ initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessName);
+ }
+
+ @Test
+ public void assertChannelProcessMemoryIsUpdatedWithPIDset() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_PROCESS_MEMORY;
+ String acceptedItemType = "Number:DataAmount";
+ // The pid of the System idle process in Windows
+ int pid = 0;
+
+ QuantityType<DataAmount> mockedProcessMemory = new QuantityType<>(450, Units.MEBIBYTE);
+ when(mockedSystemInfo.getProcessMemoryUsage(pid)).thenReturn(mockedProcessMemory);
+
+ initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessMemory);
+ }
+
+ @Test
+ public void assertChannelProcessLoadIsUpdatedWithPIDset() throws DeviceNotFoundException {
+ String channnelID = SystemInfoBindingConstants.CHANNEL_PROCESS_LOAD;
+ String acceptedItemType = "Number";
+ // The pid of the System idle process in Windows
+ int pid = 0;
+
+ DecimalType mockedProcessLoad = new DecimalType(3);
+ when(mockedSystemInfo.getProcessCpuUsage(pid)).thenReturn(mockedProcessLoad);
+
+ initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
+ assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessLoad);
+ }
+
+ @Test
+ public void testThingHandlesChannelPriorityChange() {
+ String priorityKey = "priority";
+ String pidKey = "pid";
+ String initialPriority = DEFAULT_CHANNEL_TEST_PRIORITY; // Evaluates to High
+ String newPriority = "Low";
+
+ String acceptedItemType = "Number";
+ initializeThingWithChannel(DEFAULT_TEST_CHANNEL_ID, acceptedItemType);
+
+ Thing thing = systeminfoThing;
+ if (thing == null) {
+ throw new AssertionError("Thing is null");
+ }
+ Channel channel = thing.getChannel(DEFAULT_TEST_CHANNEL_ID);
+ if (channel == null) {
+ throw new AssertionError("Channel '" + DEFAULT_TEST_CHANNEL_ID + "' is null");
+ }
+
+ ThingHandler thingHandler = thing.getHandler();
+ if (thingHandler == null) {
+ throw new AssertionError("Thing handler is null");
+ }
+ if (!(thingHandler.getClass().equals(SystemInfoHandler.class))) {
+ throw new AssertionError("Thing handler not of class SystemInfoHandler");
+ }
+ SystemInfoHandler handler = (SystemInfoHandler) thingHandler;
+ waitForAssert(() -> {
+ assertThat("The initial priority of channel " + channel.getUID() + " is not as expected.",
+ channel.getConfiguration().get(priorityKey), is(equalTo(initialPriority)));
+ assertThat(handler.getHighPriorityChannels().contains(channel.getUID()), is(true));
+ });
+
+ // Change the priority of a channel, keep the pid
+ Configuration updatedConfig = new Configuration();
+ updatedConfig.put(priorityKey, newPriority);
+ updatedConfig.put(pidKey, channel.getConfiguration().get(pidKey));
+ Channel updatedChannel = ChannelBuilder.create(channel.getUID(), channel.getAcceptedItemType())
+ .withType(channel.getChannelTypeUID()).withKind(channel.getKind()).withConfiguration(updatedConfig)
+ .build();
+
+ Thing updatedThing = ThingBuilder.create(thing.getThingTypeUID(), thing.getUID())
+ .withConfiguration(thing.getConfiguration()).withChannel(updatedChannel).build();
+
+ handler.thingUpdated(updatedThing);
+
+ waitForAssert(() -> {
+ assertThat("The prority of the channel was not updated: ", channel.getConfiguration().get(priorityKey),
+ is(equalTo(newPriority)));
+ assertThat(handler.getLowPriorityChannels().contains(channel.getUID()), is(true));
+ });
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.systeminfo.test;
-
-import static java.lang.Thread.sleep;
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.mockito.Mockito.*;
-
-import java.math.BigDecimal;
-import java.net.UnknownHostException;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import javax.measure.quantity.ElectricPotential;
-import javax.measure.quantity.Frequency;
-import javax.measure.quantity.Temperature;
-import javax.measure.quantity.Time;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
-import org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants;
-import org.openhab.binding.systeminfo.internal.SysteminfoHandlerFactory;
-import org.openhab.binding.systeminfo.internal.discovery.SysteminfoDiscoveryService;
-import org.openhab.binding.systeminfo.internal.handler.SysteminfoHandler;
-import org.openhab.binding.systeminfo.internal.model.DeviceNotFoundException;
-import org.openhab.binding.systeminfo.internal.model.OSHISysteminfo;
-import org.openhab.binding.systeminfo.internal.model.SysteminfoInterface;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.openhab.core.config.discovery.inbox.Inbox;
-import org.openhab.core.config.discovery.inbox.InboxPredicates;
-import org.openhab.core.i18n.UnitProvider;
-import org.openhab.core.items.GenericItem;
-import org.openhab.core.items.ItemNotFoundException;
-import org.openhab.core.items.ItemRegistry;
-import org.openhab.core.library.dimension.DataAmount;
-import org.openhab.core.library.items.NumberItem;
-import org.openhab.core.library.items.StringItem;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.test.java.JavaOSGiTest;
-import org.openhab.core.test.storage.VolatileStorageService;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.ManagedThingProvider;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingProvider;
-import org.openhab.core.thing.ThingRegistry;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerFactory;
-import org.openhab.core.thing.binding.builder.ChannelBuilder;
-import org.openhab.core.thing.binding.builder.ThingBuilder;
-import org.openhab.core.thing.link.ItemChannelLink;
-import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
-import org.openhab.core.thing.type.ChannelKind;
-import org.openhab.core.thing.type.ChannelTypeUID;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-
-/**
- * OSGi tests for the {@link SysteminfoHandler}
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Lyubomir Papazov - Created a mock systeminfo object. This way, access to the user's OS will not be required,
- * but mock data will be used instead, avoiding potential errors from the OS queries.
- * @author Wouter Born - Migrate Groovy to Java tests
- * @author Mark Herwege - Processor frequency channels
- */
-@NonNullByDefault
-@ExtendWith(MockitoExtension.class)
-@MockitoSettings(strictness = Strictness.LENIENT)
-public class SysteminfoOSGiTest extends JavaOSGiTest {
-
- private static final String DEFAULT_TEST_THING_NAME = "work";
- private static final String DEFAULT_TEST_ITEM_NAME = "test";
- private static final String DEFAULT_CHANNEL_TEST_PRIORITY = "High";
- private static final int DEFAULT_CHANNEL_PID = -1;
- private static final String DEFAULT_TEST_CHANNEL_ID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD;
- private static final int DEFAULT_DEVICE_INDEX = 0;
-
- /**
- * Refresh time in seconds for tasks with priority High.
- * Default value for the parameter interval_high in the thing configuration
- */
- private static final int DEFAULT_TEST_INTERVAL_HIGH = 1;
-
- /**
- * Refresh time in seconds for tasks with priority Medium.
- */
- private static final int DEFAULT_TEST_INTERVAL_MEDIUM = 3;
-
- private @Nullable Thing systeminfoThing;
- private @Nullable GenericItem testItem;
-
- private @Mock @NonNullByDefault({}) OSHISysteminfo mockedSystemInfo;
- private @NonNullByDefault({}) SysteminfoHandlerFactory systeminfoHandlerFactory;
- private @NonNullByDefault({}) ThingRegistry thingRegistry;
- private @NonNullByDefault({}) ItemRegistry itemRegistry;
- private @NonNullByDefault({}) ManagedThingProvider managedThingProvider;
- private @NonNullByDefault({}) ManagedItemChannelLinkProvider itemChannelLinkProvider;
- private @NonNullByDefault({}) UnitProvider unitProvider;
- private @NonNullByDefault({}) VolatileStorageService volatileStorageService;
-
- @BeforeEach
- public void setUp() {
- volatileStorageService = new VolatileStorageService();
- registerService(volatileStorageService);
-
- // Preparing the mock with OS properties, that are used in the initialize method of SysteminfoHandler
- // Make this lenient because the assertInvalidThingConfigurationValuesAreHandled test does not require them
- lenient().when(mockedSystemInfo.getCpuLogicalCores()).thenReturn(new DecimalType(1));
- lenient().when(mockedSystemInfo.getCpuPhysicalCores()).thenReturn(new DecimalType(1));
- lenient().when(mockedSystemInfo.getOsFamily()).thenReturn(new StringType("Mock OS"));
- lenient().when(mockedSystemInfo.getOsManufacturer()).thenReturn(new StringType("Mock OS Manufacturer"));
- lenient().when(mockedSystemInfo.getOsVersion()).thenReturn(new StringType("Mock Os Version"));
- // Following mock method returns will make sure the thing does not get recreated with extra channels
- lenient().when(mockedSystemInfo.getNetworkIFCount()).thenReturn(1);
- lenient().when(mockedSystemInfo.getDisplayCount()).thenReturn(1);
- lenient().when(mockedSystemInfo.getFileOSStoreCount()).thenReturn(1);
- lenient().when(mockedSystemInfo.getPowerSourceCount()).thenReturn(1);
- lenient().when(mockedSystemInfo.getDriveCount()).thenReturn(1);
- lenient().when(mockedSystemInfo.getFanCount()).thenReturn(1);
-
- registerService(mockedSystemInfo);
-
- waitForAssert(() -> {
- systeminfoHandlerFactory = getService(ThingHandlerFactory.class, SysteminfoHandlerFactory.class);
- assertThat(systeminfoHandlerFactory, is(notNullValue()));
- });
-
- if (systeminfoHandlerFactory != null) {
- // Unbind oshiSystemInfo service and bind the mock service to make the systeminfo binding tests independent
- // of the external OSHI library
- SysteminfoInterface oshiSystemInfo = getService(SysteminfoInterface.class);
- if (oshiSystemInfo != null) {
- systeminfoHandlerFactory.unbindSystemInfo(oshiSystemInfo);
- }
- systeminfoHandlerFactory.bindSystemInfo(mockedSystemInfo);
- }
-
- waitForAssert(() -> {
- thingRegistry = getService(ThingRegistry.class);
- assertThat(thingRegistry, is(notNullValue()));
- });
-
- waitForAssert(() -> {
- itemRegistry = getService(ItemRegistry.class);
- assertThat(itemRegistry, is(notNullValue()));
- });
-
- waitForAssert(() -> {
- managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
- assertThat(managedThingProvider, is(notNullValue()));
- });
-
- waitForAssert(() -> {
- itemChannelLinkProvider = getService(ManagedItemChannelLinkProvider.class);
- assertThat(itemChannelLinkProvider, is(notNullValue()));
- });
-
- waitForAssert(() -> {
- unitProvider = getService(UnitProvider.class);
- assertThat(unitProvider, is(notNullValue()));
- });
- }
-
- @AfterEach
- public void tearDown() {
- Thing thing = systeminfoThing;
- if (thing != null) {
- // Remove the systeminfo thing. The handler will also be disposed automatically
- Thing removedThing = thingRegistry.forceRemove(thing.getUID());
- assertThat("The systeminfo thing cannot be deleted", removedThing, is(notNullValue()));
- waitForAssert(() -> {
- ThingHandler systemInfoHandler = thing.getHandler();
- assertThat(systemInfoHandler, is(nullValue()));
- });
- managedThingProvider.remove(thing.getUID());
- }
-
- if (testItem != null) {
- itemRegistry.remove(DEFAULT_TEST_ITEM_NAME);
- }
-
- unregisterService(mockedSystemInfo);
- unregisterService(volatileStorageService);
- }
-
- private void initializeThingWithChannelAndPID(String channelID, String acceptedItemType, int pid) {
- Configuration thingConfig = new Configuration();
- thingConfig.put(SysteminfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
- thingConfig.put(SysteminfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
- String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
-
- initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
- }
-
- private void initializeThingWithChannelAndPriority(String channelID, String acceptedItemType, String priority) {
- Configuration thingConfig = new Configuration();
- thingConfig.put(SysteminfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
- thingConfig.put(SysteminfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
- int pid = DEFAULT_CHANNEL_PID;
-
- initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
- }
-
- private void initializeThingWithConfiguration(Configuration config) {
- String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
- String channelID = DEFAULT_TEST_CHANNEL_ID;
- String acceptedItemType = "String";
- int pid = DEFAULT_CHANNEL_PID;
-
- initializeThing(config, channelID, acceptedItemType, priority, pid);
- }
-
- private void initializeThingWithChannel(String channelID, String acceptedItemType) {
- Configuration thingConfig = new Configuration();
- thingConfig.put(SysteminfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_HIGH));
- thingConfig.put(SysteminfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
- new BigDecimal(DEFAULT_TEST_INTERVAL_MEDIUM));
-
- String priority = DEFAULT_CHANNEL_TEST_PRIORITY;
- int pid = DEFAULT_CHANNEL_PID;
- initializeThing(thingConfig, channelID, acceptedItemType, priority, pid);
- }
-
- private void initializeThing(Configuration thingConfiguration, String channelID, String acceptedItemType,
- String priority, int pid) {
- ThingTypeUID thingTypeUID = SysteminfoBindingConstants.THING_TYPE_COMPUTER;
- ThingUID thingUID = new ThingUID(thingTypeUID, DEFAULT_TEST_THING_NAME);
-
- ChannelUID channelUID = new ChannelUID(thingUID, channelID);
- String channelTypeId = channelUID.getIdWithoutGroup();
- if ("load1".equals(channelTypeId) || "load5".equals(channelTypeId) || "load15".equals(channelTypeId)) {
- channelTypeId = "loadAverage";
- }
- ChannelTypeUID channelTypeUID = new ChannelTypeUID(SysteminfoBindingConstants.BINDING_ID, channelTypeId);
- Configuration channelConfig = new Configuration();
- channelConfig.put("priority", priority);
- channelConfig.put("pid", new BigDecimal(pid));
- Channel channel = ChannelBuilder.create(channelUID, acceptedItemType).withType(channelTypeUID)
- .withKind(ChannelKind.STATE).withConfiguration(channelConfig).build();
-
- ThingBuilder thingBuilder = ThingBuilder.create(thingTypeUID, thingUID).withConfiguration(thingConfiguration)
- .withChannel(channel);
- // Make sure the thingTypeVersion matches the highest version in the update instructions of the binding to avoid
- // new channels being added and the thing not initializing
- thingBuilder = thingBuilder.withProperties(Map.of("thingTypeVersion", "1"));
- Thing thing = thingBuilder.build();
- systeminfoThing = thing;
-
- managedThingProvider.add(thing);
-
- waitForAssert(() -> {
- SysteminfoHandler handler = (SysteminfoHandler) thing.getHandler();
- assertThat(handler, is(notNullValue()));
- });
-
- waitForAssert(() -> {
- assertThat("Thing is not initialized, before an Item is created", thing.getStatus(),
- anyOf(equalTo(ThingStatus.OFFLINE), equalTo(ThingStatus.ONLINE)));
- });
-
- intializeItem(channelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
- }
-
- private void assertItemState(String acceptedItemType, String itemName, String priority, State expectedState) {
- Thing thing = systeminfoThing;
- if (thing == null) {
- throw new AssertionError("Thing is null");
- }
- waitForAssert(() -> {
- ThingStatusDetail thingStatusDetail = thing.getStatusInfo().getStatusDetail();
- String description = thing.getStatusInfo().getDescription();
- assertThat("Thing status detail is " + thingStatusDetail + " with description " + description,
- thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
- });
- // The binding starts all refresh tasks in SysteminfoHandler.scheduleUpdates() after this delay !
- try {
- sleep(SysteminfoHandler.WAIT_TIME_CHANNEL_ITEM_LINK_INIT * 1000);
- } catch (InterruptedException e) {
- throw new AssertionError("Interrupted while sleeping");
- }
-
- GenericItem item;
- try {
- item = (GenericItem) itemRegistry.getItem(itemName);
- } catch (ItemNotFoundException e) {
- throw new AssertionError("Item not found in registry");
- }
-
- int waitTime;
- if ("High".equals(priority)) {
- waitTime = DEFAULT_TEST_INTERVAL_HIGH * 1000;
- } else if ("Medium".equals(priority)) {
- waitTime = DEFAULT_TEST_INTERVAL_MEDIUM * 1000;
- } else {
- waitTime = 100;
- }
-
- waitForAssert(() -> {
- State itemState = item.getState();
- assertThat(itemState, is(equalTo(expectedState)));
- }, waitTime, DFL_SLEEP_TIME);
- }
-
- private void intializeItem(ChannelUID channelUID, String itemName, String acceptedItemType) {
- GenericItem item = null;
- if (acceptedItemType.startsWith("Number")) {
- item = new NumberItem(acceptedItemType, itemName, unitProvider);
- } else if ("String".equals(acceptedItemType)) {
- item = new StringItem(itemName);
- }
- if (item == null) {
- throw new AssertionError("Item is null");
- }
- itemRegistry.add(item);
- testItem = item;
-
- itemChannelLinkProvider.add(new ItemChannelLink(itemName, channelUID));
- }
-
- @Test
- public void assertInvalidThingConfigurationValuesAreHandled() {
- Configuration configuration = new Configuration();
-
- // invalid value - must be positive
- int refreshIntervalHigh = -5;
- configuration.put(SysteminfoBindingConstants.HIGH_PRIORITY_REFRESH_TIME, new BigDecimal(refreshIntervalHigh));
-
- int refreshIntervalMedium = 3;
- configuration.put(SysteminfoBindingConstants.MEDIUM_PRIORITY_REFRESH_TIME,
- new BigDecimal(refreshIntervalMedium));
- initializeThingWithConfiguration(configuration);
-
- testInvalidConfiguration();
- }
-
- private void testInvalidConfiguration() {
- waitForAssert(() -> {
- Thing thing = systeminfoThing;
- if (thing != null) {
- assertThat("Invalid configuration is used !", thing.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
- assertThat(thing.getStatusInfo().getStatusDetail(),
- is(equalTo(ThingStatusDetail.HANDLER_INITIALIZING_ERROR)));
- assertThat(thing.getStatusInfo().getDescription(), is(equalTo("@text/offline.cannot-initialize")));
- }
- });
- }
-
- @Test
- public void assertMediumPriorityChannelIsUpdated() {
- String channnelID = DEFAULT_TEST_CHANNEL_ID;
- String acceptedItemType = "Number";
- String priority = "Medium";
-
- initializeThingWithChannelAndPriority(channnelID, acceptedItemType, priority);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, priority, UnDefType.UNDEF);
- }
-
- @Test
- public void assertStateOfSecondDeviceIsUpdated() {
- // This test assumes that at least 2 network interfaces are present on the test platform
- int deviceIndex = 1;
- String channnelID = "network" + deviceIndex + "#mac";
- String acceptedItemType = "String";
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, UnDefType.UNDEF);
- }
-
- @Test
- public void assertChannelCpuMaxFreq() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_MAXFREQ;
- String acceptedItemType = "Number:Frequency";
-
- QuantityType<Frequency> mockedCpuMaxFreqValue = new QuantityType<>(2500, Units.HERTZ);
- when(mockedSystemInfo.getCpuMaxFreq()).thenReturn(mockedCpuMaxFreqValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuMaxFreqValue);
- }
-
- @Test
- public void assertChannelCpuFreq() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_FREQ;
- String acceptedItemType = "Number:Frequency";
-
- QuantityType<Frequency> mockedCpuFreqValue = new QuantityType<>(2500, Units.HERTZ);
- when(mockedSystemInfo.getCpuFreq(0)).thenReturn(mockedCpuFreqValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuFreqValue);
- }
-
- @Test
- public void assertChannelCpuLoadIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD;
- String acceptedItemType = "Number";
-
- PercentType mockedCpuLoadValue = new PercentType(9);
- when(mockedSystemInfo.getSystemCpuLoad()).thenReturn(mockedCpuLoadValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoadValue);
- }
-
- @Test
- public void assertChannelCpuLoad1IsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD_1;
- String acceptedItemType = "Number";
-
- DecimalType mockedCpuLoad1Value = new DecimalType(1.1);
- when(mockedSystemInfo.getCpuLoad1()).thenReturn(mockedCpuLoad1Value);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad1Value);
- }
-
- @Test
- public void assertChannelCpuLoad5IsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD_5;
- String acceptedItemType = "Number";
-
- DecimalType mockedCpuLoad5Value = new DecimalType(5.5);
- when(mockedSystemInfo.getCpuLoad5()).thenReturn(mockedCpuLoad5Value);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad5Value);
- }
-
- @Test
- public void assertChannelCpuLoad15IsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD_15;
- String acceptedItemType = "Number";
-
- DecimalType mockedCpuLoad15Value = new DecimalType(15.15);
- when(mockedSystemInfo.getCpuLoad15()).thenReturn(mockedCpuLoad15Value);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuLoad15Value);
- }
-
- @Test
- public void assertChannelCpuThreadsIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_THREADS;
- String acceptedItemType = "Number";
-
- DecimalType mockedCpuThreadsValue = new DecimalType(16);
- when(mockedSystemInfo.getCpuThreads()).thenReturn(mockedCpuThreadsValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuThreadsValue);
- }
-
- @Test
- public void assertChannelCpuUptimeIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_UPTIME;
- String acceptedItemType = "Number:Time";
-
- QuantityType<Time> mockedCpuUptimeValue = new QuantityType<>(100, Units.MINUTE);
- when(mockedSystemInfo.getCpuUptime()).thenReturn(mockedCpuUptimeValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuUptimeValue);
- }
-
- @Test
- public void assertChannelCpuDescriptionIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_DESCRIPTION;
- String acceptedItemType = "String";
-
- StringType mockedCpuDescriptionValue = new StringType("Mocked Cpu Descr");
- when(mockedSystemInfo.getCpuDescription()).thenReturn(mockedCpuDescriptionValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedCpuDescriptionValue);
- }
-
- @Test
- public void assertChannelCpuNameIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_NAME;
- String acceptedItemType = "String";
-
- StringType mockedCpuNameValue = new StringType("Mocked Cpu Name");
- when(mockedSystemInfo.getCpuName()).thenReturn(mockedCpuNameValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuNameValue);
- }
-
- @Test
- public void assertChannelMemoryAvailableIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_MEMORY_AVAILABLE;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedMemoryAvailableValue = new QuantityType<>(1000, Units.MEBIBYTE);
- when(mockedSystemInfo.getMemoryAvailable()).thenReturn(mockedMemoryAvailableValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedMemoryAvailableValue);
- }
-
- @Test
- public void assertChannelMemoryUsedIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_MEMORY_USED;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedMemoryUsedValue = new QuantityType<>(24, Units.MEBIBYTE);
- when(mockedSystemInfo.getMemoryUsed()).thenReturn(mockedMemoryUsedValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedMemoryUsedValue);
- }
-
- @Test
- public void assertChannelMemoryTotalIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_MEMORY_TOTAL;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedMemoryTotalValue = new QuantityType<>(1024, Units.MEBIBYTE);
- when(mockedSystemInfo.getMemoryTotal()).thenReturn(mockedMemoryTotalValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedMemoryTotalValue);
- }
-
- @Test
- public void assertChannelMemoryAvailablePercentIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_MEMORY_AVAILABLE_PERCENT;
- String acceptedItemType = "Number";
-
- PercentType mockedMemoryAvailablePercentValue = new PercentType(97);
- when(mockedSystemInfo.getMemoryAvailablePercent()).thenReturn(mockedMemoryAvailablePercentValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedMemoryAvailablePercentValue);
- }
-
- @Test
- public void assertChannelSwapAvailableIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SWAP_AVAILABLE;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedSwapAvailableValue = new QuantityType<>(482, Units.MEBIBYTE);
- when(mockedSystemInfo.getSwapAvailable()).thenReturn(mockedSwapAvailableValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedSwapAvailableValue);
- }
-
- @Test
- public void assertChannelSwapUsedIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SWAP_USED;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedSwapUsedValue = new QuantityType<>(30, Units.MEBIBYTE);
- when(mockedSystemInfo.getSwapUsed()).thenReturn(mockedSwapUsedValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedSwapUsedValue);
- }
-
- @Test
- public void assertChannelSwapTotalIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SWAP_TOTAL;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedSwapTotalValue = new QuantityType<>(512, Units.MEBIBYTE);
- when(mockedSystemInfo.getSwapTotal()).thenReturn(mockedSwapTotalValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedSwapTotalValue);
- }
-
- @Test
- public void assertChannelSwapAvailablePercentIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SWAP_AVAILABLE_PERCENT;
- String acceptedItemType = "Number";
-
- PercentType mockedSwapAvailablePercentValue = new PercentType(94);
- when(mockedSystemInfo.getSwapAvailablePercent()).thenReturn(mockedSwapAvailablePercentValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedSwapAvailablePercentValue);
- }
-
- @Test
- public void assertChannelStorageNameIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_NAME;
- String acceptedItemType = "String";
-
- StringType mockedStorageName = new StringType("Mocked Storage Name");
- when(mockedSystemInfo.getStorageName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageName);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedStorageName);
- }
-
- @Test
- public void assertChannelStorageTypeIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_TYPE;
- String acceptedItemType = "String";
-
- StringType mockedStorageType = new StringType("Mocked Storage Type");
- when(mockedSystemInfo.getStorageType(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageType);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedStorageType);
- }
-
- @Test
- public void assertChannelStorageDescriptionIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_DESCRIPTION;
- String acceptedItemType = "String";
-
- StringType mockedStorageDescription = new StringType("Mocked Storage Description");
- when(mockedSystemInfo.getStorageDescription(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageDescription);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedStorageDescription);
- }
-
- @Test
- public void assertChannelStorageAvailableIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_AVAILABLE;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedStorageAvailableValue = new QuantityType<>(2000, Units.MEBIBYTE);
- when(mockedSystemInfo.getStorageAvailable(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageAvailableValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedStorageAvailableValue);
- }
-
- @Test
- public void assertChannelStorageUsedIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_USED;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedStorageUsedValue = new QuantityType<>(500, Units.MEBIBYTE);
- when(mockedSystemInfo.getStorageUsed(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageUsedValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedStorageUsedValue);
- }
-
- @Test
- public void assertChannelStorageTotalIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_TOTAL;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedStorageTotalValue = new QuantityType<>(2500, Units.MEBIBYTE);
- when(mockedSystemInfo.getStorageTotal(DEFAULT_DEVICE_INDEX)).thenReturn(mockedStorageTotalValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedStorageTotalValue);
- }
-
- @Test
- public void assertChannelStorageAvailablePercentIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_STORAGE_AVAILABLE_PERCENT;
- String acceptedItemType = "Number";
-
- PercentType mockedStorageAvailablePercent = new PercentType(20);
- when(mockedSystemInfo.getStorageAvailablePercent(DEFAULT_DEVICE_INDEX))
- .thenReturn(mockedStorageAvailablePercent);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedStorageAvailablePercent);
- }
-
- @Test
- public void assertChannelDriveNameIsUpdated() throws DeviceNotFoundException {
- String channelID = SysteminfoBindingConstants.CHANNEL_DRIVE_NAME;
- String acceptedItemType = "String";
-
- StringType mockedDriveNameValue = new StringType("Mocked Drive Name");
- when(mockedSystemInfo.getDriveName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveNameValue);
-
- initializeThingWithChannel(channelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDriveNameValue);
- }
-
- @Test
- public void assertChannelDriveModelIsUpdated() throws DeviceNotFoundException {
- String channelID = SysteminfoBindingConstants.CHANNEL_DRIVE_MODEL;
- String acceptedItemType = "String";
-
- StringType mockedDriveModelValue = new StringType("Mocked Drive Model");
- when(mockedSystemInfo.getDriveModel(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveModelValue);
-
- initializeThingWithChannel(channelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDriveModelValue);
- }
-
- @Test
- public void assertChannelDriveSerialIsUpdated() throws DeviceNotFoundException {
- String channelID = SysteminfoBindingConstants.CHANNEL_DRIVE_SERIAL;
- String acceptedItemType = "String";
-
- StringType mockedDriveSerialNumber = new StringType("Mocked Drive Serial Number");
- when(mockedSystemInfo.getDriveSerialNumber(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDriveSerialNumber);
-
- initializeThingWithChannel(channelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedDriveSerialNumber);
- }
-
- // Re-enable this previously disabled test, as it is not relying on hardware anymore, but a mocked object
- // There is a bug opened for this issue - https://github.com/dblock/oshi/issues/185
- @Test
- public void assertChannelSensorsCpuTempIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SENSORS_CPU_TEMPERATURE;
- String acceptedItemType = "Number:Temperature";
-
- QuantityType<Temperature> mockedSensorsCpuTemperatureValue = new QuantityType<>(60, SIUnits.CELSIUS);
- when(mockedSystemInfo.getSensorsCpuTemperature()).thenReturn(mockedSensorsCpuTemperatureValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedSensorsCpuTemperatureValue);
- }
-
- @Test
- public void assertChannelSensorsCpuVoltageIsUpdated() {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SENOSRS_CPU_VOLTAGE;
- String acceptedItemType = "Number:ElectricPotential";
-
- QuantityType<ElectricPotential> mockedSensorsCpuVoltageValue = new QuantityType<>(1000, Units.VOLT);
- when(mockedSystemInfo.getSensorsCpuVoltage()).thenReturn(mockedSensorsCpuVoltageValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedSensorsCpuVoltageValue);
- }
-
- @Test
- public void assertChannelSensorsFanSpeedIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_SENSORS_FAN_SPEED;
- String acceptedItemType = "Number";
-
- DecimalType mockedSensorsCpuFanSpeedValue = new DecimalType(180);
- when(mockedSystemInfo.getSensorsFanSpeed(DEFAULT_DEVICE_INDEX)).thenReturn(mockedSensorsCpuFanSpeedValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedSensorsCpuFanSpeedValue);
- }
-
- @Test
- public void assertChannelBatteryNameIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_BATTERY_NAME;
- String acceptedItemType = "String";
-
- StringType mockedBatteryName = new StringType("Mocked Battery Name");
- when(mockedSystemInfo.getBatteryName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedBatteryName);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedBatteryName);
- }
-
- @Test
- public void assertChannelBatteryRemainingCapacityIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_BATTERY_REMAINING_CAPACITY;
- String acceptedItemType = "Number";
-
- PercentType mockedBatteryRemainingCapacity = new PercentType(20);
- when(mockedSystemInfo.getBatteryRemainingCapacity(DEFAULT_DEVICE_INDEX))
- .thenReturn(mockedBatteryRemainingCapacity);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedBatteryRemainingCapacity);
- }
-
- @Test
- public void assertChannelBatteryRemainingTimeIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_BATTERY_REMAINING_TIME;
- String acceptedItemType = "Number:Time";
-
- QuantityType<Time> mockedBatteryRemainingTime = new QuantityType<>(3600, Units.MINUTE);
- when(mockedSystemInfo.getBatteryRemainingTime(DEFAULT_DEVICE_INDEX)).thenReturn(mockedBatteryRemainingTime);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedBatteryRemainingTime);
- }
-
- @Test
- public void assertChannelDisplayInformationIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_DISPLAY_INFORMATION;
- String acceptedItemType = "String";
-
- StringType mockedDisplayInfo = new StringType("Mocked Display Information");
- when(mockedSystemInfo.getDisplayInformation(DEFAULT_DEVICE_INDEX)).thenReturn(mockedDisplayInfo);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedDisplayInfo);
- }
-
- @Test
- public void assertChannelNetworkIpIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_IP;
- String acceptedItemType = "String";
-
- StringType mockedNetworkIp = new StringType("192.168.1.0");
- when(mockedSystemInfo.getNetworkIp(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkIp);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkIp);
- }
-
- @Test
- public void assertChannelNetworkMacIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_MAC;
- String acceptedItemType = "String";
-
- StringType mockedNetworkMacValue = new StringType("AB-10-11-12-13-14");
- when(mockedSystemInfo.getNetworkMac(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkMacValue);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkMacValue);
- }
-
- @Test
- public void assertChannelNetworkDataSentIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_DATA_SENT;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedNetworkDataSent = new QuantityType<>(1000, Units.MEBIBYTE);
- when(mockedSystemInfo.getNetworkDataSent(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkDataSent);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkDataSent);
- }
-
- @Test
- public void assertChannelNetworkDataReceivedIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_DATA_RECEIVED;
- String acceptedItemType = "Number:DataAmount";
-
- QuantityType<DataAmount> mockedNetworkDataReceiveed = new QuantityType<>(800, Units.MEBIBYTE);
- when(mockedSystemInfo.getNetworkDataReceived(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkDataReceiveed);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedNetworkDataReceiveed);
- }
-
- @Test
- public void assertChannelNetworkPacketsSentIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_PACKETS_SENT;
- String acceptedItemType = "Number";
-
- DecimalType mockedNetworkPacketsSent = new DecimalType(50);
- when(mockedSystemInfo.getNetworkPacketsSent(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkPacketsSent);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedNetworkPacketsSent);
- }
-
- @Test
- public void assertChannelNetworkPacketsReceivedIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_PACKETS_RECEIVED;
- String acceptedItemType = "Number";
-
- DecimalType mockedNetworkPacketsReceived = new DecimalType(48);
- when(mockedSystemInfo.getNetworkPacketsReceived(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkPacketsReceived);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedNetworkPacketsReceived);
- }
-
- @Test
- public void assertChannelNetworkNetworkNameIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_NAME;
- String acceptedItemType = "String";
-
- StringType mockedNetworkName = new StringType("MockN-AQ34");
- when(mockedSystemInfo.getNetworkName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkName);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedNetworkName);
- }
-
- @Test
- public void assertChannelNetworkNetworkDisplayNameIsUpdated() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_NETWORK_ADAPTER_NAME;
- String acceptedItemType = "String";
-
- StringType mockedNetworkAdapterName = new StringType("Mocked Network Adapter Name");
- when(mockedSystemInfo.getNetworkDisplayName(DEFAULT_DEVICE_INDEX)).thenReturn(mockedNetworkAdapterName);
-
- initializeThingWithChannel(channnelID, acceptedItemType);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedNetworkAdapterName);
- }
-
- class SysteminfoDiscoveryServiceMock extends SysteminfoDiscoveryService {
- String hostname;
-
- SysteminfoDiscoveryServiceMock(String hostname) {
- super();
- this.hostname = hostname;
- }
-
- @Override
- protected String getHostName() throws UnknownHostException {
- if ("unresolved".equals(hostname)) {
- throw new UnknownHostException();
- }
- return hostname;
- }
-
- @Override
- public void startScan() {
- super.startScan();
- }
- }
-
- @Test
- public void testDiscoveryWithInvalidHostname() {
- String hostname = "Hilo.fritz.box";
- String expectedHostname = "Hilo_fritz_box";
-
- testDiscoveryService(expectedHostname, hostname);
- }
-
- @Test
- public void testDiscoveryWithValidHostname() {
- String hostname = "MyComputer";
- String expectedHostname = "MyComputer";
-
- testDiscoveryService(expectedHostname, hostname);
- }
-
- @Test
- public void testDiscoveryWithUnresolvedHostname() {
- String hostname = "unresolved";
- String expectedHostname = SysteminfoDiscoveryService.DEFAULT_THING_ID;
-
- testDiscoveryService(expectedHostname, hostname);
- }
-
- @Test
- public void testDiscoveryWithEmptyHostnameString() {
- String hostname = "";
- String expectedHostname = SysteminfoDiscoveryService.DEFAULT_THING_ID;
-
- testDiscoveryService(expectedHostname, hostname);
- }
-
- private void testDiscoveryService(String expectedHostname, String hostname) {
- SysteminfoDiscoveryService discoveryService = getService(DiscoveryService.class,
- SysteminfoDiscoveryService.class);
- waitForAssert(() -> {
- assertThat(discoveryService, is(notNullValue()));
- });
- SysteminfoDiscoveryServiceMock discoveryServiceMock = new SysteminfoDiscoveryServiceMock(hostname);
- if (discoveryService != null) {
- unregisterService(DiscoveryService.class);
- }
- registerService(discoveryServiceMock, DiscoveryService.class.getName(), new Hashtable<>());
-
- ThingTypeUID computerType = SysteminfoBindingConstants.THING_TYPE_COMPUTER;
- ThingUID computerUID = new ThingUID(computerType, expectedHostname);
-
- discoveryServiceMock.startScan();
-
- Inbox inbox = getService(Inbox.class);
- waitForAssert(() -> {
- assertThat(inbox, is(notNullValue()));
- });
-
- if (inbox == null) {
- return;
- }
-
- waitForAssert(() -> {
- List<DiscoveryResult> results = inbox.stream().filter(InboxPredicates.forThingUID(computerUID)).toList();
- assertFalse(results.isEmpty(), "No Thing with UID " + computerUID.getAsString() + " in inbox");
- });
-
- inbox.approve(computerUID, SysteminfoDiscoveryService.DEFAULT_THING_LABEL, null);
-
- waitForAssert(() -> {
- systeminfoThing = thingRegistry.get(computerUID);
- assertThat(systeminfoThing, is(notNullValue()));
- });
-
- Thing thing = systeminfoThing;
- if (thing == null) {
- return;
- }
-
- waitForAssert(() -> {
- assertThat("Thing is not initialized.", thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
- });
- }
-
- @Test
- public void assertChannelProcessThreadsIsUpdatedWithPIDse() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_PROCESS_THREADS;
- String acceptedItemType = "Number";
- // The pid of the System idle process in Windows
- int pid = 0;
-
- DecimalType mockedProcessThreadsCount = new DecimalType(4);
- when(mockedSystemInfo.getProcessThreads(pid)).thenReturn(mockedProcessThreadsCount);
-
- initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY,
- mockedProcessThreadsCount);
- }
-
- @Test
- public void assertChannelProcessPathIsUpdatedWithPIDset() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_PROCESS_PATH;
- String acceptedItemType = "String";
- // The pid of the System idle process in Windows
- int pid = 0;
-
- StringType mockedProcessPath = new StringType("C:\\Users\\MockedUser\\Process");
- when(mockedSystemInfo.getProcessPath(pid)).thenReturn(mockedProcessPath);
-
- initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessPath);
- }
-
- @Test
- public void assertChannelProcessNameIsUpdatedWithPIDset() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_PROCESS_NAME;
- String acceptedItemType = "String";
- // The pid of the System idle process in Windows
- int pid = 0;
-
- StringType mockedProcessName = new StringType("MockedProcess.exe");
- when(mockedSystemInfo.getProcessName(pid)).thenReturn(mockedProcessName);
-
- initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessName);
- }
-
- @Test
- public void assertChannelProcessMemoryIsUpdatedWithPIDset() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_PROCESS_MEMORY;
- String acceptedItemType = "Number:DataAmount";
- // The pid of the System idle process in Windows
- int pid = 0;
-
- QuantityType<DataAmount> mockedProcessMemory = new QuantityType<>(450, Units.MEBIBYTE);
- when(mockedSystemInfo.getProcessMemoryUsage(pid)).thenReturn(mockedProcessMemory);
-
- initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessMemory);
- }
-
- @Test
- public void assertChannelProcessLoadIsUpdatedWithPIDset() throws DeviceNotFoundException {
- String channnelID = SysteminfoBindingConstants.CHANNEL_PROCESS_LOAD;
- String acceptedItemType = "Number";
- // The pid of the System idle process in Windows
- int pid = 0;
-
- DecimalType mockedProcessLoad = new DecimalType(3);
- when(mockedSystemInfo.getProcessCpuUsage(pid)).thenReturn(mockedProcessLoad);
-
- initializeThingWithChannelAndPID(channnelID, acceptedItemType, pid);
- assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedProcessLoad);
- }
-
- @Test
- public void testThingHandlesChannelPriorityChange() {
- String priorityKey = "priority";
- String pidKey = "pid";
- String initialPriority = DEFAULT_CHANNEL_TEST_PRIORITY; // Evaluates to High
- String newPriority = "Low";
-
- String acceptedItemType = "Number";
- initializeThingWithChannel(DEFAULT_TEST_CHANNEL_ID, acceptedItemType);
-
- Thing thing = systeminfoThing;
- if (thing == null) {
- throw new AssertionError("Thing is null");
- }
- Channel channel = thing.getChannel(DEFAULT_TEST_CHANNEL_ID);
- if (channel == null) {
- throw new AssertionError("Channel '" + DEFAULT_TEST_CHANNEL_ID + "' is null");
- }
-
- ThingHandler thingHandler = thing.getHandler();
- if (thingHandler == null) {
- throw new AssertionError("Thing handler is null");
- }
- if (!(thingHandler.getClass().equals(SysteminfoHandler.class))) {
- throw new AssertionError("Thing handler not of class SysteminfoHandler");
- }
- SysteminfoHandler handler = (SysteminfoHandler) thingHandler;
- waitForAssert(() -> {
- assertThat("The initial priority of channel " + channel.getUID() + " is not as expected.",
- channel.getConfiguration().get(priorityKey), is(equalTo(initialPriority)));
- assertThat(handler.getHighPriorityChannels().contains(channel.getUID()), is(true));
- });
-
- // Change the priority of a channel, keep the pid
- Configuration updatedConfig = new Configuration();
- updatedConfig.put(priorityKey, newPriority);
- updatedConfig.put(pidKey, channel.getConfiguration().get(pidKey));
- Channel updatedChannel = ChannelBuilder.create(channel.getUID(), channel.getAcceptedItemType())
- .withType(channel.getChannelTypeUID()).withKind(channel.getKind()).withConfiguration(updatedConfig)
- .build();
-
- Thing updatedThing = ThingBuilder.create(thing.getThingTypeUID(), thing.getUID())
- .withConfiguration(thing.getConfiguration()).withChannel(updatedChannel).build();
-
- handler.thingUpdated(updatedThing);
-
- waitForAssert(() -> {
- assertThat("The prority of the channel was not updated: ", channel.getConfiguration().get(priorityKey),
- is(equalTo(newPriority)));
- assertThat(handler.getLowPriorityChannels().contains(channel.getUID()), is(true));
- });
- }
-}