ChannelType type;
ChannelTypeUID channelTypeUID;
- channelUID = new ChannelUID(component.getGroupUID(), channelID);
+ channelUID = component.buildChannelUID(channelID);
channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID,
channelUID.getGroupId() + "_" + channelID);
channelState = new HomeAssistantChannelState(
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory.ComponentConfiguration;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
+import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.thing.ChannelGroupUID;
+import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.type.ChannelDefinition;
import org.openhab.core.thing.type.ChannelGroupDefinition;
import org.openhab.core.thing.type.ChannelGroupType;
// Component location fields
private final ComponentConfiguration componentConfiguration;
- protected final ChannelGroupTypeUID channelGroupTypeUID;
- protected final ChannelGroupUID channelGroupUID;
+ protected final @Nullable ChannelGroupTypeUID channelGroupTypeUID;
+ protected final @Nullable ChannelGroupUID channelGroupUID;
protected final HaID haID;
// Channels and configuration
this.haID = componentConfiguration.getHaID();
- String groupId = this.haID.getGroupId(channelConfiguration.getUniqueId());
+ if (channelConfiguration.getName() != null) {
+ String groupId = this.haID.getGroupId(channelConfiguration.getUniqueId());
- this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
- this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
+ this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
+ this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
+ } else {
+ this.channelGroupTypeUID = null;
+ this.channelGroupUID = null;
+ }
this.configSeen = false;
* @param channelTypeProvider The channel type provider
*/
public void addChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
- channelTypeProvider.setChannelGroupType(getGroupTypeUID(), getType());
+ ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
+ if (groupTypeUID != null) {
+ channelTypeProvider.setChannelGroupType(groupTypeUID, Objects.requireNonNull(getType()));
+ }
channels.values().forEach(v -> v.addChannelTypes(channelTypeProvider));
}
*/
public void removeChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
channels.values().forEach(v -> v.removeChannelTypes(channelTypeProvider));
- channelTypeProvider.removeChannelGroupType(getGroupTypeUID());
+ ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
+ if (groupTypeUID != null) {
+ channelTypeProvider.removeChannelGroupType(groupTypeUID);
+ }
+ }
+
+ public ChannelUID buildChannelUID(String channelID) {
+ final ChannelGroupUID groupUID = channelGroupUID;
+ if (groupUID != null) {
+ return new ChannelUID(groupUID, channelID);
+ }
+ return new ChannelUID(componentConfiguration.getThingUID(), channelID);
}
/**
* Each HomeAssistant component corresponds to a Channel Group Type.
*/
- public ChannelGroupTypeUID getGroupTypeUID() {
+ public @Nullable ChannelGroupTypeUID getGroupTypeUID() {
return channelGroupTypeUID;
}
/**
* The unique id of this component.
*/
- public ChannelGroupUID getGroupUID() {
+ public @Nullable ChannelGroupUID getGroupUID() {
return channelGroupUID;
}
* Component (Channel Group) name.
*/
public String getName() {
- return channelConfiguration.getName();
+ String result = channelConfiguration.getName();
+
+ Device device = channelConfiguration.getDevice();
+ if (result == null && device != null) {
+ result = device.getName();
+ }
+ if (result == null) {
+ result = haID.objectID;
+ }
+ return result;
}
/**
/**
* Return the channel group type.
*/
- public ChannelGroupType getType() {
+ public @Nullable ChannelGroupType getType() {
+ ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
+ if (groupTypeUID == null) {
+ return null;
+ }
final List<ChannelDefinition> channelDefinitions = channels.values().stream().map(ComponentChannel::type)
.collect(Collectors.toList());
- return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, getName())
- .withChannelDefinitions(channelDefinitions).build();
+ return ChannelGroupTypeBuilder.instance(groupTypeUID, getName()).withChannelDefinitions(channelDefinitions)
+ .build();
+ }
+
+ public List<ChannelDefinition> getChannels() {
+ return channels.values().stream().map(ComponentChannel::type).collect(Collectors.toList());
}
/**
/**
* Return the channel group definition for this component.
*/
- public ChannelGroupDefinition getGroupDefinition() {
- return new ChannelGroupDefinition(channelGroupUID.getId(), getGroupTypeUID(), getName(), null);
+ public @Nullable ChannelGroupDefinition getGroupDefinition() {
+ ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
+ if (groupTypeUID == null) {
+ return null;
+ }
+ return new ChannelGroupDefinition(channelGroupUID.getId(), groupTypeUID, getName(), null);
}
public HaID getHaID() {
final String[] stateEnum = { channelConfiguration.stateDisarmed, channelConfiguration.stateArmedHome,
channelConfiguration.stateArmedAway, channelConfiguration.statePending,
channelConfiguration.stateTriggered };
- buildChannel(STATE_CHANNEL_ID, new TextValue(stateEnum), channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(STATE_CHANNEL_ID, new TextValue(stateEnum), getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.build();
String commandTopic = channelConfiguration.commandTopic;
if (commandTopic != null) {
buildChannel(SWITCH_DISARM_CHANNEL_ID, new TextValue(new String[] { channelConfiguration.payloadDisarm }),
- channelConfiguration.getName(), componentConfiguration.getUpdateListener())
+ getName(), componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
buildChannel(SWITCH_ARM_HOME_CHANNEL_ID,
- new TextValue(new String[] { channelConfiguration.payloadArmHome }), channelConfiguration.getName(),
+ new TextValue(new String[] { channelConfiguration.payloadArmHome }), getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
buildChannel(SWITCH_ARM_AWAY_CHANNEL_ID,
- new TextValue(new String[] { channelConfiguration.payloadArmAway }), channelConfiguration.getName(),
+ new TextValue(new String[] { channelConfiguration.payloadArmAway }), getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
}
ImageValue value = new ImageValue();
- buildChannel(CAMERA_CHANNEL_ID, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener()).stateTopic(channelConfiguration.topic).build();
+ buildChannel(CAMERA_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
+ .stateTopic(channelConfiguration.topic).build();
}
}
@Nullable String commandTopic, @Nullable String stateTemplate, @Nullable String stateTopic,
@Nullable Predicate<Command> commandFilter) {
if ((commandTopic != null && !commandTopic.isBlank()) || (stateTopic != null && !stateTopic.isBlank())) {
- return buildChannel(channelId, valueState, channelConfiguration.getName(), channelStateUpdateListener)
+ return buildChannel(channelId, valueState, getName(), channelStateUpdateListener)
.stateTopic(stateTopic, stateTemplate, channelConfiguration.getValueTemplate())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos(),
commandTemplate)
RollershutterValue value = new RollershutterValue(channelConfiguration.payloadOpen,
channelConfiguration.payloadClose, channelConfiguration.payloadStop);
- buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(SWITCH_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
colorValue.update(newOnState);
}
- listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
+ listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID),
state.equals(OnOffType.ON) ? newOnState : HSBType.BLACK);
} else if (brightnessChannel != null) {
listener.updateChannelState(new ChannelUID(channel.getThingUID(), BRIGHTNESS_CHANNEL_ID),
colorValue.update(new HSBType(DecimalType.ZERO, PercentType.ZERO,
(PercentType) brightnessValue.getChannelState()));
}
- listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
- colorValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
} else {
listener.updateChannelState(channel, state);
}
HSBType xyColor = HSBType.fromXY(x, y);
colorValue.update(new HSBType(xyColor.getHue(), xyColor.getSaturation(), brightness));
}
- listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
- colorValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
return;
case RGB_CHANNEL_ID:
colorValue.update((HSBType) state);
- listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
- colorValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
break;
case RGBW_CHANNEL_ID:
case RGBWW_CHANNEL_ID:
value = new TextValue();
}
- buildChannel(channelConfiguration.type, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(channelConfiguration.type, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.topic, channelConfiguration.getValueTemplate()).trigger(true).build();
}
}
super(componentConfiguration, ChannelConfiguration.class);
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
- buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(SWITCH_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
colorModeValue.getChannelState());
if (hasColorChannel) {
- listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID), colorValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
} else if (brightnessChannel != null) {
- listener.updateChannelState(new ChannelUID(getGroupUID(), BRIGHTNESS_CHANNEL_ID),
- brightnessValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(BRIGHTNESS_CHANNEL_ID), brightnessValue.getChannelState());
} else {
- listener.updateChannelState(new ChannelUID(getGroupUID(), ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
+ listener.updateChannelState(buildChannelUID(ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
}
}
}
}
buildChannel(SWITCH_CHANNEL_ID,
- new OnOffValue(channelConfiguration.payloadLock, channelConfiguration.payloadUnlock),
- channelConfiguration.getName(), componentConfiguration.getUpdateListener())
+ new OnOffValue(channelConfiguration.payloadLock, channelConfiguration.payloadUnlock), getName(),
+ componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
NumberValue value = new NumberValue(channelConfiguration.min, channelConfiguration.max,
channelConfiguration.step, UnitUtils.parseUnit(channelConfiguration.unitOfMeasurement));
- buildChannel(NUMBER_CHANNEL_ID, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(NUMBER_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
TextValue value = new TextValue(channelConfiguration.options);
- buildChannel(SELECT_CHANNEL_ID, value, channelConfiguration.getName(),
- componentConfiguration.getUpdateListener())
+ buildChannel(SELECT_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
boolean trigger = TRIGGER_ICONS.matcher(icon).matches();
- buildChannel(SENSOR_CHANNEL_ID, value, channelConfiguration.getName(),
- getListener(componentConfiguration, value))
+ buildChannel(SENSOR_CHANNEL_ID, value, getName(), getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.trigger(trigger).build();
}
ChannelStateUpdateListener channelStateUpdateListener, @Nullable String commandTemplate,
@Nullable String commandTopic, @Nullable String stateTemplate, @Nullable String stateTopic) {
if ((commandTopic != null && !commandTopic.isBlank()) || (stateTopic != null && !stateTopic.isBlank())) {
- return buildChannel(channelId, valueState, channelConfiguration.getName(), channelStateUpdateListener)
+ return buildChannel(channelId, valueState, getName(), channelStateUpdateListener)
.stateTopic(stateTopic, stateTemplate, channelConfiguration.getValueTemplate())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos(),
commandTemplate)
@NonNullByDefault
public abstract class AbstractChannelConfiguration {
public static final char PARENT_TOPIC_PLACEHOLDER = '~';
+ private static final String DEFAULT_THING_NAME = "Home Assistant Device";
- protected String name;
+ protected @Nullable String name;
protected String icon = "";
protected int qos; // defaults to 0 according to HA specification
if (result == null) {
result = name;
}
+ if (result == null) {
+ result = DEFAULT_THING_NAME;
+ }
return result;
}
return properties;
}
- public String getName() {
+ public @Nullable String getName() {
return name;
}
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelGroupUID;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelDefinition;
import org.openhab.core.thing.type.ChannelGroupDefinition;
-import org.openhab.core.thing.type.ChannelGroupType;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.util.ThingHelper;
import org.slf4j.Logger;
protected final DiscoverComponents discoverComponents;
private final Gson gson;
- protected final Map<String, AbstractComponent<?>> haComponents = new HashMap<>();
+ protected final Map<@Nullable String, AbstractComponent<?>> haComponents = new HashMap<>();
protected HandlerConfiguration config = new HandlerConfiguration();
private Set<HaID> discoveryHomeAssistantIDs = new HashSet<>();
for (Channel channel : thing.getChannels()) {
final String groupID = channel.getUID().getGroupId();
- if (groupID == null) {
- logger.warn("Channel {} has no groupd ID", channel.getLabel());
- continue;
- }
// Already restored component?
@Nullable
AbstractComponent<?> component = haComponents.get(groupID);
try {
component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
scheduler, gson, transformationServiceProvider);
- haComponents.put(component.getGroupUID().getId(), component);
+ final ChannelGroupUID groupUID = component.getGroupUID();
+ String id = null;
+ if (groupUID != null) {
+ id = groupUID.getId();
+ }
+ haComponents.put(id, component);
component.addChannelTypes(channelTypeProvider);
} catch (ConfigurationException e) {
logger.error("Cannot not restore component {}: {}", thing, e.getMessage());
@Override
public @Nullable ChannelState getChannelState(ChannelUID channelUID) {
String groupID = channelUID.getGroupId();
- if (groupID == null) {
- return null;
- }
AbstractComponent<?> component;
synchronized (haComponents) { // sync whenever discoverComponents is started
component = haComponents.get(groupID);
synchronized (haComponents) { // sync whenever discoverComponents is started
for (AbstractComponent<?> discovered : discoveredComponentsList) {
- AbstractComponent<?> known = haComponents.get(discovered.getGroupUID().getId());
+ final ChannelGroupUID groupUID = discovered.getGroupUID();
+ String id = null;
+ if (groupUID != null) {
+ id = groupUID.getId();
+ }
+ AbstractComponent<?> known = haComponents.get(id);
// Is component already known?
if (known != null) {
if (discovered.getConfigHash() != known.getConfigHash()) {
// Add channel and group types to the types registry
discovered.addChannelTypes(channelTypeProvider);
// Add component to the component map
- haComponents.put(discovered.getGroupUID().getId(), discovered);
+ haComponents.put(id, discovered);
// Start component / Subscribe to channel topics
discovered.start(connection, scheduler, 0).exceptionally(e -> {
- logger.warn("Failed to start component {}", discovered.getGroupUID(), e);
+ logger.warn("Failed to start component {}", discovered.getHaID(), e);
return null;
});
// We remove all conflicting old channels, they will be re-added below based on the new discovery
logger.debug(
"Received component {} with slightly different config. Making sure we re-create conflicting channels...",
- discovered.getGroupUID());
+ discovered.getHaID());
removeJustRediscoveredChannels(discoveredChannels);
}
List<ChannelDefinition> channelDefs;
synchronized (haComponents) { // sync whenever discoverComponents is started
groupDefs = haComponents.values().stream().map(AbstractComponent::getGroupDefinition)
- .collect(Collectors.toList());
- channelDefs = haComponents.values().stream().map(AbstractComponent::getType)
- .map(ChannelGroupType::getChannelDefinitions).flatMap(List::stream)
+ .filter(Objects::nonNull).map(Objects::requireNonNull).collect(Collectors.toList());
+ channelDefs = haComponents.values().stream().map(AbstractComponent::getChannels).flatMap(List::stream)
.collect(Collectors.toList());
}
ThingType thingType = channelTypeProvider.derive(typeID, MqttBindingConstants.HOMEASSISTANT_MQTT_THING)
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
ComponentDiscovered cd = (haID, c) -> {
haComponents.put(c.getGroupUID().getId(), c);
c.addChannelTypes(channelTypeProvider);
- channelTypeProvider.setChannelGroupType(c.getGroupTypeUID(), c.getType());
+ channelTypeProvider.setChannelGroupType(Objects.requireNonNull(c.getGroupTypeUID()),
+ Objects.requireNonNull(c.getType()));
latch.countDown();
};