Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mqtt] Simplify homeassistant thing types, and use AbstractStorageBasedTypeProvider #16600

Merged
merged 11 commits into from
Aug 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,19 @@

import java.net.URI;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.internal.MqttThingHandlerFactory;
import org.openhab.core.storage.StorageService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider;
import org.openhab.core.thing.binding.ThingTypeProvider;
import org.openhab.core.thing.type.ChannelGroupType;
import org.openhab.core.thing.type.ChannelGroupTypeProvider;
import org.openhab.core.thing.type.ChannelGroupTypeUID;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeProvider;
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;
Expand All @@ -45,89 +42,24 @@
* This provider is started on-demand only, as soon as {@link MqttThingHandlerFactory} or an extension requires it.
*
* @author David Graeff - Initial contribution
* @author Cody Cutrer - Use AbstractStorageBasedTypeProvider
*
*/
@NonNullByDefault
@Component(immediate = false, service = { ThingTypeProvider.class, ChannelTypeProvider.class,
ChannelGroupTypeProvider.class, MqttChannelTypeProvider.class })
public class MqttChannelTypeProvider implements ThingTypeProvider, ChannelGroupTypeProvider, ChannelTypeProvider {
private final ThingTypeRegistry typeRegistry;

private final Map<ChannelTypeUID, ChannelType> types = new ConcurrentHashMap<>();
private final Map<ChannelGroupTypeUID, ChannelGroupType> groups = new ConcurrentHashMap<>();
private final Map<ThingTypeUID, ThingType> things = new ConcurrentHashMap<>();
public class MqttChannelTypeProvider extends AbstractStorageBasedTypeProvider {
private final ThingTypeRegistry thingTypeRegistry;

@Activate
public MqttChannelTypeProvider(@Reference ThingTypeRegistry typeRegistry) {
super();
this.typeRegistry = typeRegistry;
}

@Override
public Collection<ChannelType> getChannelTypes(@Nullable Locale locale) {
return types.values();
}

@Override
public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
return types.get(channelTypeUID);
}

@Override
public @Nullable ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID,
@Nullable Locale locale) {
return groups.get(channelGroupTypeUID);
}

@Override
public Collection<ChannelGroupType> getChannelGroupTypes(@Nullable Locale locale) {
return groups.values();
}

@Override
public Collection<ThingType> getThingTypes(@Nullable Locale locale) {
return things.values();
}

public Set<ThingTypeUID> getThingTypeUIDs() {
return things.keySet();
}

@Override
public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) {
return things.get(thingTypeUID);
}

public void removeChannelType(ChannelTypeUID uid) {
types.remove(uid);
}

public void removeChannelGroupType(ChannelGroupTypeUID uid) {
groups.remove(uid);
}

public void setChannelGroupType(ChannelGroupTypeUID uid, ChannelGroupType type) {
groups.put(uid, type);
}

public void setChannelType(ChannelTypeUID uid, ChannelType type) {
types.put(uid, type);
}

public void removeThingType(ThingTypeUID uid) {
things.remove(uid);
}

public void setThingType(ThingTypeUID uid, ThingType type) {
things.put(uid, type);
}

public void setThingTypeIfAbsent(ThingTypeUID uid, ThingType type) {
things.putIfAbsent(uid, type);
public MqttChannelTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
@Reference StorageService storageService) {
super(storageService);
this.thingTypeRegistry = thingTypeRegistry;
}

public ThingTypeBuilder derive(ThingTypeUID newTypeId, ThingTypeUID baseTypeId) {
ThingType baseType = typeRegistry.getThingType(baseTypeId);
ThingType baseType = thingTypeRegistry.getThingType(baseTypeId);

ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeId, baseType.getLabel())
.withChannelGroupDefinitions(baseType.getChannelGroupDefinitions())
Expand Down Expand Up @@ -155,4 +87,25 @@ public ThingTypeBuilder derive(ThingTypeUID newTypeId, ThingTypeUID baseTypeId)

return result;
}

public void updateChannelGroupTypesForPrefix(String prefix, Collection<ChannelGroupType> types) {
Collection<ChannelGroupType> oldCgts = channelGroupTypesForPrefix(prefix);

Set<ChannelGroupTypeUID> oldUids = oldCgts.stream().map(ChannelGroupType::getUID).collect(Collectors.toSet());
Collection<ChannelGroupTypeUID> uids = types.stream().map(ChannelGroupType::getUID).toList();

oldUids.removeAll(uids);
// oldUids now contains only UIDs that no longer exist. so remove them
oldUids.forEach(this::removeChannelGroupType);
types.forEach(this::putChannelGroupType);
}

public void removeChannelGroupTypesForPrefix(String prefix) {
channelGroupTypesForPrefix(prefix).forEach(cgt -> removeChannelGroupType(cgt.getUID()));
}

private Collection<ChannelGroupType> channelGroupTypesForPrefix(String prefix) {
return getChannelGroupTypes(null).stream().filter(cgt -> cgt.getUID().getId().startsWith(prefix + "_"))
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
*/
package org.openhab.binding.mqtt.generic.tools;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -64,6 +67,24 @@ public Stream<T> stream() {
return map.values().stream();
}

/**
* Streams the objects in this map in the order of the given keys.
*
* Extraneous keys are ignored, and missing keys are included at the end in unspecified order.
*
* @param order The keys in the order they should be streamed
*/
public Stream<T> stream(Collection<String> order) {
// need to make a copy to avoid editing `map` itself
Set<String> missingKeys = new HashSet<>(map.keySet());
missingKeys.removeAll(order);
Stream<T> result = order.stream().map(k -> map.get(k)).filter(Objects::nonNull).map(Objects::requireNonNull);
if (!missingKeys.isEmpty()) {
result = Stream.concat(result, missingKeys.stream().map(k -> map.get(k)).map(Objects::requireNonNull));
}
return result;
}

/**
* Modifies the map in way that it matches the entries of the given childIDs.
*
Expand Down Expand Up @@ -134,4 +155,8 @@ public void clear() {
public void put(String key, T value) {
map.put(key, value);
}

public Set<String> keySet() {
return map.keySet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,4 @@ public class MqttBindingConstants {

// List of all Thing Type UIDs
public static final ThingTypeUID HOMEASSISTANT_MQTT_THING = new ThingTypeUID(BINDING_ID, "homeassistant");

public static final String CONFIG_HA_CHANNEL = "channel-type:mqtt:ha-channel";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
Expand All @@ -26,12 +27,11 @@
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.transform.TransformationHelper;
import org.openhab.core.transform.TransformationService;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;

/**
Expand All @@ -43,10 +43,22 @@
@Component(service = ThingHandlerFactory.class)
@NonNullByDefault
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider;
private final MqttChannelTypeProvider typeProvider;
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
private final ChannelTypeRegistry channelTypeRegistry;

private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
.of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet());

@Activate
public MqttThingHandlerFactory(final @Reference MqttChannelTypeProvider typeProvider,
final @Reference MqttChannelStateDescriptionProvider stateDescriptionProvider,
final @Reference ChannelTypeRegistry channelTypeRegistry) {
this.typeProvider = typeProvider;
this.stateDescriptionProvider = stateDescriptionProvider;
this.channelTypeRegistry = channelTypeRegistry;
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || isHomeassistantDynamicType(thingTypeUID);
Expand All @@ -57,33 +69,13 @@ private boolean isHomeassistantDynamicType(ThingTypeUID thingTypeUID) {
&& thingTypeUID.getId().startsWith(MqttBindingConstants.HOMEASSISTANT_MQTT_THING.getId());
}

@Activate
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
}

@Deactivate
@Override
protected void deactivate(ComponentContext componentContext) {
super.deactivate(componentContext);
}

@Reference
protected void setChannelProvider(MqttChannelTypeProvider provider) {
this.typeProvider = provider;
}

protected void unsetChannelProvider(MqttChannelTypeProvider provider) {
this.typeProvider = null;
}

@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (supportsThingType(thingTypeUID)) {
return new HomeAssistantThingHandler(thing, typeProvider, this, 10000, 2000);
return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
this, 10000, 2000);
}
return null;
}
Expand Down
Loading