mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-25 14:55:55 +01:00
[mqtt.homeassistant] fix newStyleChannels (#17491)
* [mqtt.homeassistant] fix newStyleChannels * further simplify channel IDs Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
parent
90442a3864
commit
31f6cda174
@ -51,13 +51,13 @@ public class ChannelState implements MqttMessageSubscriber {
|
|||||||
|
|
||||||
// Immutable channel configuration
|
// Immutable channel configuration
|
||||||
protected final boolean readOnly;
|
protected final boolean readOnly;
|
||||||
protected final ChannelUID channelUID;
|
|
||||||
protected final ChannelConfig config;
|
protected final ChannelConfig config;
|
||||||
|
|
||||||
/** Channel value **/
|
/** Channel value **/
|
||||||
protected final Value cachedValue;
|
protected final Value cachedValue;
|
||||||
|
|
||||||
// Runtime variables
|
// Runtime variables
|
||||||
|
protected ChannelUID channelUID;
|
||||||
private @Nullable MqttBrokerConnection connection;
|
private @Nullable MqttBrokerConnection connection;
|
||||||
protected final ChannelTransformation incomingTransformation;
|
protected final ChannelTransformation incomingTransformation;
|
||||||
protected final ChannelTransformation outgoingTransformation;
|
protected final ChannelTransformation outgoingTransformation;
|
||||||
@ -132,6 +132,11 @@ public class ChannelState implements MqttMessageSubscriber {
|
|||||||
return channelUID;
|
return channelUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the UID of the channel changed after it was initially created
|
||||||
|
public void setChannelUID(ChannelUID channelUID) {
|
||||||
|
this.channelUID = channelUID;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Incoming message from the MqttBrokerConnection
|
* Incoming message from the MqttBrokerConnection
|
||||||
*
|
*
|
||||||
|
@ -58,7 +58,7 @@ import org.openhab.core.types.StateDescription;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ComponentChannel {
|
public class ComponentChannel {
|
||||||
private final ChannelState channelState;
|
private final ChannelState channelState;
|
||||||
private final Channel channel;
|
private Channel channel;
|
||||||
private final @Nullable StateDescription stateDescription;
|
private final @Nullable StateDescription stateDescription;
|
||||||
private final @Nullable CommandDescription commandDescription;
|
private final @Nullable CommandDescription commandDescription;
|
||||||
private final ChannelStateUpdateListener channelStateUpdateListener;
|
private final ChannelStateUpdateListener channelStateUpdateListener;
|
||||||
@ -77,6 +77,18 @@ public class ComponentChannel {
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetUID(ChannelUID channelUID) {
|
||||||
|
channel = ChannelBuilder.create(channelUID, channel.getAcceptedItemType()).withType(channel.getChannelTypeUID())
|
||||||
|
.withKind(channel.getKind()).withLabel(Objects.requireNonNull(channel.getLabel()))
|
||||||
|
.withConfiguration(channel.getConfiguration()).withAutoUpdatePolicy(channel.getAutoUpdatePolicy())
|
||||||
|
.build();
|
||||||
|
channelState.setChannelUID(channelUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearConfiguration() {
|
||||||
|
channel = ChannelBuilder.create(channel).withConfiguration(new Configuration()).build();
|
||||||
|
}
|
||||||
|
|
||||||
public ChannelState getState() {
|
public ChannelState getState() {
|
||||||
return channelState;
|
return channelState;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.stream.Stream;
|
|||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||||
|
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||||
import org.openhab.binding.mqtt.generic.values.Value;
|
import org.openhab.binding.mqtt.generic.values.Value;
|
||||||
@ -39,7 +40,6 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AvailabilityMo
|
|||||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
|
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
|
||||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
import org.openhab.core.thing.ChannelGroupUID;
|
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.binding.generic.ChannelTransformation;
|
import org.openhab.core.thing.binding.generic.ChannelTransformation;
|
||||||
import org.openhab.core.thing.type.ChannelDefinition;
|
import org.openhab.core.thing.type.ChannelDefinition;
|
||||||
@ -65,7 +65,6 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
|
|
||||||
// Component location fields
|
// Component location fields
|
||||||
protected final ComponentConfiguration componentConfiguration;
|
protected final ComponentConfiguration componentConfiguration;
|
||||||
protected final @Nullable ChannelGroupUID channelGroupUID;
|
|
||||||
protected final HaID haID;
|
protected final HaID haID;
|
||||||
|
|
||||||
// Channels and configuration
|
// Channels and configuration
|
||||||
@ -79,14 +78,10 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
protected final C channelConfiguration;
|
protected final C channelConfiguration;
|
||||||
|
|
||||||
protected boolean configSeen;
|
protected boolean configSeen;
|
||||||
protected final boolean singleChannelComponent;
|
protected final boolean newStyleChannels;
|
||||||
protected final String groupId;
|
|
||||||
protected final String uniqueId;
|
protected final String uniqueId;
|
||||||
|
protected @Nullable String groupId;
|
||||||
public AbstractComponent(ComponentFactory.ComponentConfiguration componentConfiguration, Class<C> clazz,
|
protected String componentId;
|
||||||
boolean newStyleChannels) {
|
|
||||||
this(componentConfiguration, clazz, newStyleChannels, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates component based on generic configuration and component configuration type.
|
* Creates component based on generic configuration and component configuration type.
|
||||||
@ -98,9 +93,9 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
* (only if newStyleChannels is true)
|
* (only if newStyleChannels is true)
|
||||||
*/
|
*/
|
||||||
public AbstractComponent(ComponentFactory.ComponentConfiguration componentConfiguration, Class<C> clazz,
|
public AbstractComponent(ComponentFactory.ComponentConfiguration componentConfiguration, Class<C> clazz,
|
||||||
boolean newStyleChannels, boolean singleChannelComponent) {
|
boolean newStyleChannels) {
|
||||||
this.componentConfiguration = componentConfiguration;
|
this.componentConfiguration = componentConfiguration;
|
||||||
this.singleChannelComponent = newStyleChannels && singleChannelComponent;
|
this.newStyleChannels = newStyleChannels;
|
||||||
|
|
||||||
this.channelConfigurationJson = componentConfiguration.getConfigJSON();
|
this.channelConfigurationJson = componentConfiguration.getConfigJSON();
|
||||||
this.channelConfiguration = componentConfiguration.getConfig(clazz);
|
this.channelConfiguration = componentConfiguration.getConfig(clazz);
|
||||||
@ -109,14 +104,16 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
this.haID = componentConfiguration.getHaID();
|
this.haID = componentConfiguration.getHaID();
|
||||||
|
|
||||||
String name = channelConfiguration.getName();
|
String name = channelConfiguration.getName();
|
||||||
if (name != null && !name.isEmpty()) {
|
if (newStyleChannels) {
|
||||||
groupId = this.haID.getGroupId(channelConfiguration.getUniqueId(), newStyleChannels);
|
// try for a simple component/group ID first; if there are conflicts
|
||||||
|
// (components of different types, but the same object id)
|
||||||
this.channelGroupUID = this.singleChannelComponent ? null
|
// we'll resolve them later
|
||||||
: new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
|
groupId = componentId = haID.objectID.replace('-', '_');
|
||||||
|
} else if (name != null && !name.isEmpty()) {
|
||||||
|
groupId = componentId = this.haID.getGroupId(channelConfiguration.getUniqueId(), false);
|
||||||
} else {
|
} else {
|
||||||
this.groupId = this.singleChannelComponent ? haID.component : "";
|
groupId = null;
|
||||||
this.channelGroupUID = null;
|
componentId = "";
|
||||||
}
|
}
|
||||||
uniqueId = this.haID.getGroupId(channelConfiguration.getUniqueId(), false);
|
uniqueId = this.haID.getGroupId(channelConfiguration.getUniqueId(), false);
|
||||||
|
|
||||||
@ -155,10 +152,30 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void finalizeChannels() {
|
||||||
|
if (!newStyleChannels) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channels.size() == 1) {
|
||||||
|
groupId = null;
|
||||||
|
channels.values().forEach(c -> c.resetUID(buildChannelUID(componentId)));
|
||||||
|
} else {
|
||||||
|
// only the first channel needs to persist the configuration
|
||||||
|
channels.values().stream().skip(1).forEach(c -> {
|
||||||
|
c.clearConfiguration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resolveConflict() {
|
||||||
|
componentId = this.haID.getGroupId(channelConfiguration.getUniqueId(), newStyleChannels);
|
||||||
|
channels.values().forEach(c -> c.resetUID(buildChannelUID(c.getChannel().getUID().getIdWithoutGroup())));
|
||||||
|
}
|
||||||
|
|
||||||
protected ComponentChannel.Builder buildChannel(String channelID, ComponentChannelType channelType,
|
protected ComponentChannel.Builder buildChannel(String channelID, ComponentChannelType channelType,
|
||||||
Value valueState, String label, ChannelStateUpdateListener channelStateUpdateListener) {
|
Value valueState, String label, ChannelStateUpdateListener channelStateUpdateListener) {
|
||||||
if (singleChannelComponent) {
|
if (groupId == null) {
|
||||||
channelID = groupId;
|
channelID = componentId;
|
||||||
}
|
}
|
||||||
return new ComponentChannel.Builder(this, channelID, channelType.getChannelTypeUID(), valueState, label,
|
return new ComponentChannel.Builder(this, channelID, channelType.getChannelTypeUID(), valueState, label,
|
||||||
channelStateUpdateListener);
|
channelStateUpdateListener);
|
||||||
@ -216,15 +233,19 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ChannelUID buildChannelUID(String channelID) {
|
public ChannelUID buildChannelUID(String channelID) {
|
||||||
final ChannelGroupUID groupUID = channelGroupUID;
|
final String localGroupID = groupId;
|
||||||
if (groupUID != null) {
|
if (localGroupID != null) {
|
||||||
return new ChannelUID(groupUID, channelID);
|
return new ChannelUID(componentConfiguration.getThingUID(), localGroupID, channelID);
|
||||||
}
|
}
|
||||||
return new ChannelUID(componentConfiguration.getThingUID(), channelID);
|
return new ChannelUID(componentConfiguration.getThingUID(), channelID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGroupId() {
|
public String getComponentId() {
|
||||||
return groupId;
|
return componentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,7 +294,7 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
* Return the channel group type.
|
* Return the channel group type.
|
||||||
*/
|
*/
|
||||||
public @Nullable ChannelGroupType getChannelGroupType(String prefix) {
|
public @Nullable ChannelGroupType getChannelGroupType(String prefix) {
|
||||||
if (channelGroupUID == null) {
|
if (groupId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ChannelGroupTypeBuilder.instance(getChannelGroupTypeUID(prefix), getName())
|
return ChannelGroupTypeBuilder.instance(getChannelGroupTypeUID(prefix), getName())
|
||||||
@ -281,7 +302,7 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ChannelDefinition> getChannelDefinitions() {
|
public List<ChannelDefinition> getChannelDefinitions() {
|
||||||
if (channelGroupUID != null) {
|
if (groupId != null) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
return getAllChannelDefinitions();
|
return getAllChannelDefinitions();
|
||||||
@ -295,6 +316,10 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
return channels.values().stream().map(ComponentChannel::getChannel).toList();
|
return channels.values().stream().map(ComponentChannel::getChannel).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getChannelStates(Map<ChannelUID, ChannelState> states) {
|
||||||
|
channels.values().forEach(c -> states.put(c.getChannel().getUID(), c.getState()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets all channel states to state UNDEF. Call this method after the connection
|
* Resets all channel states to state UNDEF. Call this method after the connection
|
||||||
* to the MQTT broker got lost.
|
* to the MQTT broker got lost.
|
||||||
@ -307,14 +332,15 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
|||||||
* Return the channel group definition for this component.
|
* Return the channel group definition for this component.
|
||||||
*/
|
*/
|
||||||
public @Nullable ChannelGroupDefinition getGroupDefinition(String prefix) {
|
public @Nullable ChannelGroupDefinition getGroupDefinition(String prefix) {
|
||||||
if (channelGroupUID == null) {
|
String localGroupId = groupId;
|
||||||
|
if (localGroupId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ChannelGroupDefinition(channelGroupUID.getId(), getChannelGroupTypeUID(prefix), getName(), null);
|
return new ChannelGroupDefinition(localGroupId, getChannelGroupTypeUID(prefix), getName(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasGroup() {
|
public boolean hasGroup() {
|
||||||
return channelGroupUID != null;
|
return groupId != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HaID getHaID() {
|
public HaID getHaID() {
|
||||||
|
@ -97,5 +97,6 @@ public class AlarmControlPanel extends AbstractComponent<AlarmControlPanel.Chann
|
|||||||
componentConfiguration.getUpdateListener())
|
componentConfiguration.getUpdateListener())
|
||||||
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
|
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
|
||||||
}
|
}
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ public class BinarySensor extends AbstractComponent<BinarySensor.ChannelConfigur
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BinarySensor(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public BinarySensor(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
|
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
|
||||||
|
|
||||||
@ -77,6 +77,7 @@ public class BinarySensor extends AbstractComponent<BinarySensor.ChannelConfigur
|
|||||||
getListener(componentConfiguration, value))
|
getListener(componentConfiguration, value))
|
||||||
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
|
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
|
||||||
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
|
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
|
||||||
|
@ -48,7 +48,7 @@ public class Button extends AbstractComponent<Button.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Button(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Button(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
TextValue value = new TextValue(new String[] { channelConfiguration.payloadPress });
|
TextValue value = new TextValue(new String[] { channelConfiguration.payloadPress });
|
||||||
|
|
||||||
@ -57,5 +57,6 @@ public class Button extends AbstractComponent<Button.ChannelConfiguration> {
|
|||||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos())
|
channelConfiguration.getQos())
|
||||||
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,5 +46,6 @@ public class Camera extends AbstractComponent<Camera.ChannelConfiguration> {
|
|||||||
|
|
||||||
buildChannel(CAMERA_CHANNEL_ID, ComponentChannelType.IMAGE, value, getName(),
|
buildChannel(CAMERA_CHANNEL_ID, ComponentChannelType.IMAGE, value, getName(),
|
||||||
componentConfiguration.getUpdateListener()).stateTopic(channelConfiguration.topic).build();
|
componentConfiguration.getUpdateListener()).stateTopic(channelConfiguration.topic).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,6 +284,7 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
|||||||
|
|
||||||
buildOptionalChannel(POWER_CH_ID, ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
|
buildOptionalChannel(POWER_CH_ID, ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
|
||||||
channelConfiguration.powerCommandTopic, null, null, null);
|
channelConfiguration.powerCommandTopic, null, null, null);
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -150,5 +150,6 @@ public class Cover extends AbstractComponent<Cover.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).build();
|
}).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ public class DefaultSchemaLight extends Light {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasColorChannel = false;
|
||||||
if (channelConfiguration.rgbStateTopic != null || channelConfiguration.rgbCommandTopic != null) {
|
if (channelConfiguration.rgbStateTopic != null || channelConfiguration.rgbCommandTopic != null) {
|
||||||
hasColorChannel = true;
|
hasColorChannel = true;
|
||||||
hiddenChannels.add(rgbChannel = buildChannel(RGB_CHANNEL_ID, ComponentChannelType.COLOR,
|
hiddenChannels.add(rgbChannel = buildChannel(RGB_CHANNEL_ID, ComponentChannelType.COLOR,
|
||||||
@ -167,7 +168,7 @@ public class DefaultSchemaLight extends Light {
|
|||||||
if (localBrightnessChannel != null) {
|
if (localBrightnessChannel != null) {
|
||||||
hiddenChannels.add(localBrightnessChannel);
|
hiddenChannels.add(localBrightnessChannel);
|
||||||
}
|
}
|
||||||
buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
colorChannel = buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
||||||
.commandTopic(DUMMY_TOPIC, channelConfiguration.isRetain(), channelConfiguration.getQos())
|
.commandTopic(DUMMY_TOPIC, channelConfiguration.isRetain(), channelConfiguration.getQos())
|
||||||
.commandFilter(this::handleColorCommand).build();
|
.commandFilter(this::handleColorCommand).build();
|
||||||
} else if (localBrightnessChannel != null) {
|
} else if (localBrightnessChannel != null) {
|
||||||
@ -280,74 +281,74 @@ public class DefaultSchemaLight extends Light {
|
|||||||
@Override
|
@Override
|
||||||
public void updateChannelState(ChannelUID channel, State state) {
|
public void updateChannelState(ChannelUID channel, State state) {
|
||||||
ChannelStateUpdateListener listener = this.channelStateUpdateListener;
|
ChannelStateUpdateListener listener = this.channelStateUpdateListener;
|
||||||
switch (channel.getIdWithoutGroup()) {
|
String id = channel.getIdWithoutGroup();
|
||||||
case ON_OFF_CHANNEL_ID:
|
ComponentChannel localBrightnessChannel = brightnessChannel;
|
||||||
if (hasColorChannel) {
|
ComponentChannel localColorChannel = colorChannel;
|
||||||
HSBType newOnState = colorValue.getChannelState() instanceof HSBType
|
ChannelUID primaryChannelUID;
|
||||||
? (HSBType) colorValue.getChannelState()
|
if (localColorChannel != null) {
|
||||||
: HSBType.WHITE;
|
primaryChannelUID = localColorChannel.getChannel().getUID();
|
||||||
if (state.equals(OnOffType.ON)) {
|
} else if (localBrightnessChannel != null) {
|
||||||
colorValue.update(newOnState);
|
primaryChannelUID = localBrightnessChannel.getChannel().getUID();
|
||||||
}
|
} else {
|
||||||
|
primaryChannelUID = onOffChannel.getChannel().getUID();
|
||||||
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),
|
|
||||||
state.equals(OnOffType.ON) ? brightnessValue.getChannelState() : PercentType.ZERO);
|
|
||||||
} else {
|
|
||||||
listener.updateChannelState(channel, state);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case BRIGHTNESS_CHANNEL_ID:
|
|
||||||
onOffValue.update(Objects.requireNonNull(state.as(OnOffType.class)));
|
|
||||||
if (hasColorChannel) {
|
|
||||||
if (colorValue.getChannelState() instanceof HSBType) {
|
|
||||||
HSBType hsb = (HSBType) (colorValue.getChannelState());
|
|
||||||
colorValue.update(new HSBType(hsb.getHue(), hsb.getSaturation(),
|
|
||||||
(PercentType) brightnessValue.getChannelState()));
|
|
||||||
} else {
|
|
||||||
colorValue.update(new HSBType(DecimalType.ZERO, PercentType.ZERO,
|
|
||||||
(PercentType) brightnessValue.getChannelState()));
|
|
||||||
}
|
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
|
|
||||||
} else {
|
|
||||||
listener.updateChannelState(channel, state);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case COLOR_TEMP_CHANNEL_ID:
|
|
||||||
case EFFECT_CHANNEL_ID:
|
|
||||||
// Real channels; pass through
|
|
||||||
listener.updateChannelState(channel, state);
|
|
||||||
return;
|
|
||||||
case HS_CHANNEL_ID:
|
|
||||||
case XY_CHANNEL_ID:
|
|
||||||
if (brightnessValue.getChannelState() instanceof UnDefType) {
|
|
||||||
brightnessValue.update(PercentType.HUNDRED);
|
|
||||||
}
|
|
||||||
String[] split = state.toString().split(",");
|
|
||||||
if (split.length != 2) {
|
|
||||||
throw new IllegalArgumentException(state.toString() + " is not a valid string syntax");
|
|
||||||
}
|
|
||||||
float x = Float.parseFloat(split[0]);
|
|
||||||
float y = Float.parseFloat(split[1]);
|
|
||||||
PercentType brightness = (PercentType) brightnessValue.getChannelState();
|
|
||||||
if (channel.getIdWithoutGroup().equals(HS_CHANNEL_ID)) {
|
|
||||||
colorValue.update(new HSBType(new DecimalType(x), new PercentType(new BigDecimal(y)), brightness));
|
|
||||||
} else {
|
|
||||||
HSBType xyColor = HSBType.fromXY(x, y);
|
|
||||||
colorValue.update(new HSBType(xyColor.getHue(), xyColor.getSaturation(), brightness));
|
|
||||||
}
|
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
|
|
||||||
return;
|
|
||||||
case RGB_CHANNEL_ID:
|
|
||||||
colorValue.update((HSBType) state);
|
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
|
|
||||||
break;
|
|
||||||
case RGBW_CHANNEL_ID:
|
|
||||||
case RGBWW_CHANNEL_ID:
|
|
||||||
// TODO: update color value
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// on_off, brightness, and color might exist as a sole channel, which means
|
||||||
|
// they got renamed. they need to be compared against the actual UID of the
|
||||||
|
// channel. all the rest we can just check against the basic ID
|
||||||
|
if (channel.equals(onOffChannel.getChannel().getUID())) {
|
||||||
|
if (localColorChannel != null) {
|
||||||
|
HSBType newOnState = colorValue.getChannelState() instanceof HSBType newOnStateTmp ? newOnStateTmp
|
||||||
|
: HSBType.WHITE;
|
||||||
|
if (state.equals(OnOffType.ON)) {
|
||||||
|
colorValue.update(newOnState);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.updateChannelState(primaryChannelUID, state.equals(OnOffType.ON) ? newOnState : HSBType.BLACK);
|
||||||
|
} else if (brightnessChannel != null) {
|
||||||
|
listener.updateChannelState(primaryChannelUID,
|
||||||
|
state.equals(OnOffType.ON) ? brightnessValue.getChannelState() : PercentType.ZERO);
|
||||||
|
} else {
|
||||||
|
listener.updateChannelState(primaryChannelUID, state);
|
||||||
|
}
|
||||||
|
} else if (localBrightnessChannel != null && localBrightnessChannel.getChannel().getUID().equals(channel)) {
|
||||||
|
onOffValue.update(Objects.requireNonNull(state.as(OnOffType.class)));
|
||||||
|
if (localColorChannel != null) {
|
||||||
|
if (colorValue.getChannelState() instanceof HSBType hsb) {
|
||||||
|
colorValue.update(new HSBType(hsb.getHue(), hsb.getSaturation(),
|
||||||
|
(PercentType) brightnessValue.getChannelState()));
|
||||||
|
} else {
|
||||||
|
colorValue.update(new HSBType(DecimalType.ZERO, PercentType.ZERO,
|
||||||
|
(PercentType) brightnessValue.getChannelState()));
|
||||||
|
}
|
||||||
|
listener.updateChannelState(primaryChannelUID, colorValue.getChannelState());
|
||||||
|
} else {
|
||||||
|
listener.updateChannelState(primaryChannelUID, state);
|
||||||
|
}
|
||||||
|
} else if (id.equals(COLOR_TEMP_CHANNEL_ID) || channel.getIdWithoutGroup().equals(EFFECT_CHANNEL_ID)) {
|
||||||
|
// Real channels; pass through
|
||||||
|
listener.updateChannelState(channel, state);
|
||||||
|
} else if (id.equals(HS_CHANNEL_ID) || id.equals(XY_CHANNEL_ID)) {
|
||||||
|
if (brightnessValue.getChannelState() instanceof UnDefType) {
|
||||||
|
brightnessValue.update(PercentType.HUNDRED);
|
||||||
|
}
|
||||||
|
String[] split = state.toString().split(",");
|
||||||
|
if (split.length != 2) {
|
||||||
|
throw new IllegalArgumentException(state.toString() + " is not a valid string syntax");
|
||||||
|
}
|
||||||
|
float x = Float.parseFloat(split[0]);
|
||||||
|
float y = Float.parseFloat(split[1]);
|
||||||
|
PercentType brightness = (PercentType) brightnessValue.getChannelState();
|
||||||
|
if (channel.getIdWithoutGroup().equals(HS_CHANNEL_ID)) {
|
||||||
|
colorValue.update(new HSBType(new DecimalType(x), new PercentType(new BigDecimal(y)), brightness));
|
||||||
|
} else {
|
||||||
|
HSBType xyColor = HSBType.fromXY(x, y);
|
||||||
|
colorValue.update(new HSBType(xyColor.getHue(), xyColor.getSaturation(), brightness));
|
||||||
|
}
|
||||||
|
listener.updateChannelState(primaryChannelUID, colorValue.getChannelState());
|
||||||
|
} else if (id.equals(RGB_CHANNEL_ID)) {
|
||||||
|
colorValue.update((HSBType) state);
|
||||||
|
listener.updateChannelState(primaryChannelUID, colorValue.getChannelState());
|
||||||
|
}
|
||||||
|
// else rgbw channel, rgbww channel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ public class DeviceTrigger extends AbstractComponent<DeviceTrigger.ChannelConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DeviceTrigger(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public DeviceTrigger(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
if (!"trigger".equals(channelConfiguration.automationType)) {
|
if (!"trigger".equals(channelConfiguration.automationType)) {
|
||||||
throw new ConfigurationException("Component:DeviceTrigger must have automation_type 'trigger'");
|
throw new ConfigurationException("Component:DeviceTrigger must have automation_type 'trigger'");
|
||||||
@ -69,5 +69,6 @@ public class DeviceTrigger extends AbstractComponent<DeviceTrigger.ChannelConfig
|
|||||||
buildChannel(channelConfiguration.type, ComponentChannelType.TRIGGER, value, getName(),
|
buildChannel(channelConfiguration.type, ComponentChannelType.TRIGGER, value, getName(),
|
||||||
componentConfiguration.getUpdateListener())
|
componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.topic, channelConfiguration.getValueTemplate()).trigger(true).build();
|
.stateTopic(channelConfiguration.topic, channelConfiguration.getValueTemplate()).trigger(true).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ package org.openhab.binding.mqtt.homeassistant.internal.component;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
@ -118,6 +119,8 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
private final PercentageValue speedValue;
|
private final PercentageValue speedValue;
|
||||||
private State rawSpeedState;
|
private State rawSpeedState;
|
||||||
private final ComponentChannel onOffChannel;
|
private final ComponentChannel onOffChannel;
|
||||||
|
private final @Nullable ComponentChannel speedChannel;
|
||||||
|
private final ComponentChannel primaryChannel;
|
||||||
private final ChannelStateUpdateListener channelStateUpdateListener;
|
private final ChannelStateUpdateListener channelStateUpdateListener;
|
||||||
|
|
||||||
public Fan(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Fan(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
@ -144,11 +147,15 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
|
|
||||||
if (channelConfiguration.percentageCommandTopic != null) {
|
if (channelConfiguration.percentageCommandTopic != null) {
|
||||||
hiddenChannels.add(onOffChannel);
|
hiddenChannels.add(onOffChannel);
|
||||||
buildChannel(SPEED_CHANNEL_ID, ComponentChannelType.DIMMER, speedValue, "Speed", this)
|
primaryChannel = speedChannel = buildChannel(SPEED_CHANNEL_ID, ComponentChannelType.DIMMER, speedValue,
|
||||||
|
"Speed", this)
|
||||||
.stateTopic(channelConfiguration.percentageStateTopic, channelConfiguration.percentageValueTemplate)
|
.stateTopic(channelConfiguration.percentageStateTopic, channelConfiguration.percentageValueTemplate)
|
||||||
.commandTopic(channelConfiguration.percentageCommandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.percentageCommandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos(), channelConfiguration.percentageCommandTemplate)
|
channelConfiguration.getQos(), channelConfiguration.percentageCommandTemplate)
|
||||||
.commandFilter(this::handlePercentageCommand).build();
|
.commandFilter(this::handlePercentageCommand).build();
|
||||||
|
} else {
|
||||||
|
primaryChannel = onOffChannel;
|
||||||
|
speedChannel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> presetModes = channelConfiguration.presetModes;
|
List<String> presetModes = channelConfiguration.presetModes;
|
||||||
@ -184,6 +191,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
channelConfiguration.getQos(), channelConfiguration.directionCommandTemplate)
|
channelConfiguration.getQos(), channelConfiguration.directionCommandTemplate)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handlePercentageCommand(Command command) {
|
private boolean handlePercentageCommand(Command command) {
|
||||||
@ -197,7 +205,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateChannelState(ChannelUID channel, State state) {
|
public void updateChannelState(ChannelUID channel, State state) {
|
||||||
if (channel.getIdWithoutGroup().equals(SWITCH_CHANNEL_ID)) {
|
if (onOffChannel.getChannel().getUID().equals(channel)) {
|
||||||
if (rawSpeedState instanceof UnDefType && state.equals(OnOffType.ON)) {
|
if (rawSpeedState instanceof UnDefType && state.equals(OnOffType.ON)) {
|
||||||
// Assume full on if we don't yet know the actual speed
|
// Assume full on if we don't yet know the actual speed
|
||||||
state = PercentType.HUNDRED;
|
state = PercentType.HUNDRED;
|
||||||
@ -206,7 +214,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
} else {
|
} else {
|
||||||
state = rawSpeedState;
|
state = rawSpeedState;
|
||||||
}
|
}
|
||||||
} else if (channel.getIdWithoutGroup().equals(SPEED_CHANNEL_ID)) {
|
} else if (Objects.requireNonNull(speedChannel).getChannel().getUID().equals(channel)) {
|
||||||
rawSpeedState = state;
|
rawSpeedState = state;
|
||||||
if (onOffValue.getChannelState().equals(OnOffType.OFF)) {
|
if (onOffValue.getChannelState().equals(OnOffType.OFF)) {
|
||||||
// Don't pass on percentage values while the fan is off
|
// Don't pass on percentage values while the fan is off
|
||||||
@ -214,7 +222,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
speedValue.update(state);
|
speedValue.update(state);
|
||||||
channelStateUpdateListener.updateChannelState(buildChannelUID(SPEED_CHANNEL_ID), state);
|
channelStateUpdateListener.updateChannelState(primaryChannel.getChannel().getUID(), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
import org.openhab.binding.mqtt.generic.values.TextValue;
|
||||||
|
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
@ -77,6 +78,7 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void buildChannels() {
|
protected void buildChannels() {
|
||||||
|
boolean hasColorChannel = false;
|
||||||
List<LightColorMode> supportedColorModes = channelConfiguration.supportedColorModes;
|
List<LightColorMode> supportedColorModes = channelConfiguration.supportedColorModes;
|
||||||
if (supportedColorModes != null) {
|
if (supportedColorModes != null) {
|
||||||
if (LightColorMode.hasColorChannel(supportedColorModes)) {
|
if (LightColorMode.hasColorChannel(supportedColorModes)) {
|
||||||
@ -99,7 +101,7 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasColorChannel) {
|
if (hasColorChannel) {
|
||||||
buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
colorChannel = buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
||||||
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(this::handleCommand).build();
|
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(this::handleCommand).build();
|
||||||
} else if (channelConfiguration.brightness) {
|
} else if (channelConfiguration.brightness) {
|
||||||
brightnessChannel = buildChannel(BRIGHTNESS_CHANNEL_ID, ComponentChannelType.DIMMER, brightnessValue,
|
brightnessChannel = buildChannel(BRIGHTNESS_CHANNEL_ID, ComponentChannelType.DIMMER, brightnessValue,
|
||||||
@ -144,7 +146,7 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
|
|||||||
.divide(new BigDecimal(100), MathContext.DECIMAL128).intValue();
|
.divide(new BigDecimal(100), MathContext.DECIMAL128).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasColorChannel) {
|
if (colorChannel != null) {
|
||||||
json.color = new JSONState.Color();
|
json.color = new JSONState.Color();
|
||||||
if (channelConfiguration.supportedColorModes.contains(LightColorMode.COLOR_MODE_HS)) {
|
if (channelConfiguration.supportedColorModes.contains(LightColorMode.COLOR_MODE_HS)) {
|
||||||
json.color.h = state.getHue().toBigDecimal();
|
json.color.h = state.getHue().toBigDecimal();
|
||||||
@ -318,12 +320,15 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
|
|||||||
|
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_MODE_CHANNEL_ID), colorModeValue.getChannelState());
|
listener.updateChannelState(buildChannelUID(COLOR_MODE_CHANNEL_ID), colorModeValue.getChannelState());
|
||||||
|
|
||||||
if (hasColorChannel) {
|
ComponentChannel localBrightnessChannel = brightnessChannel;
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
|
ComponentChannel localColorChannel = colorChannel;
|
||||||
} else if (brightnessChannel != null) {
|
if (localColorChannel != null) {
|
||||||
listener.updateChannelState(buildChannelUID(BRIGHTNESS_CHANNEL_ID), brightnessValue.getChannelState());
|
listener.updateChannelState(localColorChannel.getChannel().getUID(), colorValue.getChannelState());
|
||||||
|
} else if (localBrightnessChannel != null) {
|
||||||
|
listener.updateChannelState(localBrightnessChannel.getChannel().getUID(),
|
||||||
|
brightnessValue.getChannelState());
|
||||||
} else {
|
} else {
|
||||||
listener.updateChannelState(buildChannelUID(ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
|
listener.updateChannelState(onOffChannel.getChannel().getUID(), onOffValue.getChannelState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,10 +228,10 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean optimistic;
|
protected final boolean optimistic;
|
||||||
protected boolean hasColorChannel = false;
|
|
||||||
|
|
||||||
protected @Nullable ComponentChannel onOffChannel;
|
protected @Nullable ComponentChannel onOffChannel;
|
||||||
protected @Nullable ComponentChannel brightnessChannel;
|
protected @Nullable ComponentChannel brightnessChannel;
|
||||||
|
protected @Nullable ComponentChannel colorChannel;
|
||||||
|
|
||||||
// State has to be stored here, in order to mux multiple
|
// State has to be stored here, in order to mux multiple
|
||||||
// MQTT sources into single OpenHAB channels
|
// MQTT sources into single OpenHAB channels
|
||||||
@ -292,6 +292,7 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
|
|||||||
colorTempValue = new NumberValue(min, max, BigDecimal.ONE, Units.MIRED);
|
colorTempValue = new NumberValue(min, max, BigDecimal.ONE, Units.MIRED);
|
||||||
|
|
||||||
buildChannels();
|
buildChannels();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void buildChannels();
|
protected abstract void buildChannels();
|
||||||
|
@ -121,6 +121,7 @@ public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).build();
|
}).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoUpdate(boolean locking) {
|
private void autoUpdate(boolean locking) {
|
||||||
|
@ -71,7 +71,7 @@ public class Number extends AbstractComponent<Number.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Number(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Number(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
||||||
: channelConfiguration.stateTopic.isBlank();
|
: channelConfiguration.stateTopic.isBlank();
|
||||||
@ -89,5 +89,6 @@ public class Number extends AbstractComponent<Number.ChannelConfiguration> {
|
|||||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
|
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
|
||||||
.build();
|
.build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ public class Scene extends AbstractComponent<Scene.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Scene(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Scene(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
TextValue value = new TextValue(new String[] { channelConfiguration.payloadOn });
|
TextValue value = new TextValue(new String[] { channelConfiguration.payloadOn });
|
||||||
|
|
||||||
@ -55,5 +55,6 @@ public class Scene extends AbstractComponent<Scene.ChannelConfiguration> {
|
|||||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos())
|
channelConfiguration.getQos())
|
||||||
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class Select extends AbstractComponent<Select.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Select(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Select(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
||||||
: channelConfiguration.stateTopic.isBlank();
|
: channelConfiguration.stateTopic.isBlank();
|
||||||
@ -73,5 +73,6 @@ public class Select extends AbstractComponent<Select.ChannelConfiguration> {
|
|||||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
|
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
|
||||||
.build();
|
.build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Sensor(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Sensor(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
Value value;
|
Value value;
|
||||||
String uom = channelConfiguration.unitOfMeasurement;
|
String uom = channelConfiguration.unitOfMeasurement;
|
||||||
@ -96,6 +96,7 @@ public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
|
|||||||
buildChannel(SENSOR_CHANNEL_ID, type, value, getName(), getListener(componentConfiguration, value))
|
buildChannel(SENSOR_CHANNEL_ID, type, value, getName(), getListener(componentConfiguration, value))
|
||||||
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
|
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
|
||||||
.trigger(trigger).build();
|
.trigger(trigger).build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
|
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
|
||||||
|
@ -61,7 +61,7 @@ public class Switch extends AbstractComponent<Switch.ChannelConfiguration> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Switch(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
public Switch(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels, true);
|
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||||
|
|
||||||
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
|
||||||
: channelConfiguration.stateTopic.isBlank();
|
: channelConfiguration.stateTopic.isBlank();
|
||||||
@ -79,5 +79,6 @@ public class Switch extends AbstractComponent<Switch.ChannelConfiguration> {
|
|||||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||||
channelConfiguration.getQos())
|
channelConfiguration.getQos())
|
||||||
.build();
|
.build();
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
|||||||
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
||||||
import org.openhab.binding.mqtt.generic.values.PercentageValue;
|
import org.openhab.binding.mqtt.generic.values.PercentageValue;
|
||||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
import org.openhab.binding.mqtt.generic.values.TextValue;
|
||||||
|
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantChannelTransformation;
|
import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantChannelTransformation;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
|
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
|
||||||
@ -85,8 +86,7 @@ public class TemplateSchemaLight extends AbstractRawSchemaLight {
|
|||||||
|
|
||||||
if (channelConfiguration.redTemplate != null && channelConfiguration.greenTemplate != null
|
if (channelConfiguration.redTemplate != null && channelConfiguration.greenTemplate != null
|
||||||
&& channelConfiguration.blueTemplate != null) {
|
&& channelConfiguration.blueTemplate != null) {
|
||||||
hasColorChannel = true;
|
colorChannel = buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
||||||
buildChannel(COLOR_CHANNEL_ID, ComponentChannelType.COLOR, colorValue, "Color", this)
|
|
||||||
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(command -> handleCommand(command)).build();
|
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(command -> handleCommand(command)).build();
|
||||||
} else if (channelConfiguration.brightnessTemplate != null) {
|
} else if (channelConfiguration.brightnessTemplate != null) {
|
||||||
brightnessChannel = buildChannel(BRIGHTNESS_CHANNEL_ID, ComponentChannelType.DIMMER, brightnessValue,
|
brightnessChannel = buildChannel(BRIGHTNESS_CHANNEL_ID, ComponentChannelType.DIMMER, brightnessValue,
|
||||||
@ -127,7 +127,7 @@ public class TemplateSchemaLight extends AbstractRawSchemaLight {
|
|||||||
binding.put(TemplateVariables.BRIGHTNESS,
|
binding.put(TemplateVariables.BRIGHTNESS,
|
||||||
state.getBrightness().toBigDecimal().multiply(factor).intValue());
|
state.getBrightness().toBigDecimal().multiply(factor).intValue());
|
||||||
}
|
}
|
||||||
if (hasColorChannel) {
|
if (colorChannel != null) {
|
||||||
int[] rgb = ColorUtil.hsbToRgb(state);
|
int[] rgb = ColorUtil.hsbToRgb(state);
|
||||||
binding.put(TemplateVariables.RED, rgb[0]);
|
binding.put(TemplateVariables.RED, rgb[0]);
|
||||||
binding.put(TemplateVariables.GREEN, rgb[1]);
|
binding.put(TemplateVariables.GREEN, rgb[1]);
|
||||||
@ -249,13 +249,15 @@ public class TemplateSchemaLight extends AbstractRawSchemaLight {
|
|||||||
colorValue.update(HSBType.fromRGB(red, green, blue));
|
colorValue.update(HSBType.fromRGB(red, green, blue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ComponentChannel localBrightnessChannel = brightnessChannel;
|
||||||
if (hasColorChannel) {
|
ComponentChannel localColorChannel = colorChannel;
|
||||||
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
|
if (localColorChannel != null) {
|
||||||
} else if (brightnessChannel != null) {
|
listener.updateChannelState(localColorChannel.getChannel().getUID(), colorValue.getChannelState());
|
||||||
listener.updateChannelState(buildChannelUID(BRIGHTNESS_CHANNEL_ID), brightnessValue.getChannelState());
|
} else if (localBrightnessChannel != null) {
|
||||||
|
listener.updateChannelState(localBrightnessChannel.getChannel().getUID(),
|
||||||
|
brightnessValue.getChannelState());
|
||||||
} else {
|
} else {
|
||||||
listener.updateChannelState(buildChannelUID(ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
|
listener.updateChannelState(onOffChannel.getChannel().getUID(), onOffValue.getChannelState());
|
||||||
}
|
}
|
||||||
|
|
||||||
template = channelConfiguration.effectTemplate;
|
template = channelConfiguration.effectTemplate;
|
||||||
|
@ -290,6 +290,7 @@ public class Vacuum extends AbstractComponent<Vacuum.ChannelConfiguration> {
|
|||||||
|
|
||||||
buildOptionalChannel(JSON_ATTRIBUTES_CH_ID, ComponentChannelType.STRING, new TextValue(), updateListener, null,
|
buildOptionalChannel(JSON_ATTRIBUTES_CH_ID, ComponentChannelType.STRING, new TextValue(), updateListener, null,
|
||||||
null, channelConfiguration.jsonAttributesTemplate, channelConfiguration.jsonAttributesTopic);
|
null, channelConfiguration.jsonAttributesTemplate, channelConfiguration.jsonAttributesTopic);
|
||||||
|
finalizeChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -33,7 +33,6 @@ import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
|||||||
import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing;
|
import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing;
|
||||||
import org.openhab.binding.mqtt.generic.utils.FutureCollector;
|
import org.openhab.binding.mqtt.generic.utils.FutureCollector;
|
||||||
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
|
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
|
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
|
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
|
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||||
@ -43,6 +42,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactor
|
|||||||
import org.openhab.binding.mqtt.homeassistant.internal.component.Update;
|
import org.openhab.binding.mqtt.homeassistant.internal.component.Update;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
|
import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
|
||||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||||
|
import org.openhab.core.config.core.Configuration;
|
||||||
import org.openhab.core.config.core.validation.ConfigValidationException;
|
import org.openhab.core.config.core.validation.ConfigValidationException;
|
||||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
@ -98,6 +98,8 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
|
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
protected final Map<@Nullable String, AbstractComponent<?>> haComponents = new HashMap<>();
|
protected final Map<@Nullable String, AbstractComponent<?>> haComponents = new HashMap<>();
|
||||||
|
protected final Map<@Nullable String, AbstractComponent<?>> haComponentsByUniqueId = new HashMap<>();
|
||||||
|
protected final Map<ChannelUID, ChannelState> channelStates = new HashMap<>();
|
||||||
|
|
||||||
protected HandlerConfiguration config = new HandlerConfiguration();
|
protected HandlerConfiguration config = new HandlerConfiguration();
|
||||||
private Set<HaID> discoveryHomeAssistantIDs = new HashSet<>();
|
private Set<HaID> discoveryHomeAssistantIDs = new HashSet<>();
|
||||||
@ -147,13 +149,21 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
ThingTypeUID typeID = getThing().getThingTypeUID();
|
ThingTypeUID typeID = getThing().getThingTypeUID();
|
||||||
for (Channel channel : thing.getChannels()) {
|
for (Channel channel : thing.getChannels()) {
|
||||||
final String groupID = channel.getUID().getGroupId();
|
final String groupID = channel.getUID().getGroupId();
|
||||||
// Already restored component?
|
if (groupID != null) {
|
||||||
@Nullable
|
// Already restored component via another channel in the component?
|
||||||
AbstractComponent<?> component = haComponents.get(groupID);
|
AbstractComponent<?> component = haComponents.get(groupID);
|
||||||
if (component != null) {
|
if (component != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Configuration channelConfig = channel.getConfiguration();
|
||||||
|
if (!channelConfig.containsKey("component")
|
||||||
|
|| !channelConfig.containsKey("objectid") | !channelConfig.containsKey("config")) {
|
||||||
|
// Must be a secondary channel
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
HaID haID = HaID.fromConfig(config.basetopic, channel.getConfiguration());
|
|
||||||
|
HaID haID = HaID.fromConfig(config.basetopic, channelConfig);
|
||||||
|
|
||||||
if (!config.topics.contains(haID.getTopic())) {
|
if (!config.topics.contains(haID.getTopic())) {
|
||||||
// don't add a component for this channel that isn't configured on the thing
|
// don't add a component for this channel that isn't configured on the thing
|
||||||
@ -164,21 +174,17 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
|
|
||||||
discoveryHomeAssistantIDs.add(haID);
|
discoveryHomeAssistantIDs.add(haID);
|
||||||
ThingUID thingUID = channel.getUID().getThingUID();
|
ThingUID thingUID = channel.getUID().getThingUID();
|
||||||
String channelConfigurationJSON = (String) channel.getConfiguration().get("config");
|
String channelConfigurationJSON = (String) channelConfig.get("config");
|
||||||
if (channelConfigurationJSON == null) {
|
try {
|
||||||
logger.warn("Provided channel does not have a 'config' configuration key!");
|
AbstractComponent<?> component = ComponentFactory.createComponent(thingUID, haID,
|
||||||
} else {
|
channelConfigurationJSON, this, this, scheduler, gson, jinjava, newStyleChannels);
|
||||||
try {
|
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
||||||
component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
|
typeID = calculateThingTypeUID(component);
|
||||||
scheduler, gson, jinjava, newStyleChannels);
|
|
||||||
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
|
||||||
typeID = calculateThingTypeUID(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
haComponents.put(component.getGroupId(), component);
|
|
||||||
} catch (ConfigurationException e) {
|
|
||||||
logger.error("Cannot restore component {}: {}", thing, e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addComponent(component);
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
logger.warn("Cannot restore component {}: {}", thing, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (updateThingType(typeID)) {
|
if (updateThingType(typeID)) {
|
||||||
@ -241,27 +247,9 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable ChannelState getChannelState(ChannelUID channelUID) {
|
public @Nullable ChannelState getChannelState(ChannelUID channelUID) {
|
||||||
String componentId;
|
|
||||||
if (channelUID.isInGroup()) {
|
|
||||||
componentId = channelUID.getGroupId();
|
|
||||||
} else {
|
|
||||||
componentId = channelUID.getId();
|
|
||||||
}
|
|
||||||
AbstractComponent<?> component;
|
|
||||||
synchronized (haComponents) { // sync whenever discoverComponents is started
|
synchronized (haComponents) { // sync whenever discoverComponents is started
|
||||||
component = haComponents.get(componentId);
|
return channelStates.get(channelUID);
|
||||||
}
|
}
|
||||||
if (component == null) {
|
|
||||||
component = haComponents.get("");
|
|
||||||
if (component == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ComponentChannel componentChannel = component.getChannel(channelUID.getIdWithoutGroup());
|
|
||||||
if (componentChannel == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return componentChannel.getState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,14 +277,18 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
||||||
typeID = calculateThingTypeUID(discovered);
|
typeID = calculateThingTypeUID(discovered);
|
||||||
}
|
}
|
||||||
String id = discovered.getGroupId();
|
AbstractComponent<?> known = haComponentsByUniqueId.get(discovered.getUniqueId());
|
||||||
AbstractComponent<?> known = haComponents.get(id);
|
|
||||||
// Is component already known?
|
// Is component already known?
|
||||||
if (known != null) {
|
if (known != null) {
|
||||||
if (discovered.getConfigHash() != known.getConfigHash()) {
|
if (discovered.getConfigHash() != known.getConfigHash()) {
|
||||||
// Don't wait for the future to complete. We are also not interested in failures.
|
// Don't wait for the future to complete. We are also not interested in failures.
|
||||||
// The component will be replaced in a moment.
|
// The component will be replaced in a moment.
|
||||||
known.stop();
|
known.stop();
|
||||||
|
haComponentsByUniqueId.remove(discovered.getUniqueId());
|
||||||
|
haComponents.remove(known.getComponentId());
|
||||||
|
if (!known.getComponentId().equals(discovered.getComponentId())) {
|
||||||
|
discovered.resolveConflict();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
known.setConfigSeen();
|
known.setConfigSeen();
|
||||||
continue;
|
continue;
|
||||||
@ -304,7 +296,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add component to the component map
|
// Add component to the component map
|
||||||
haComponents.put(id, discovered);
|
addComponent(discovered);
|
||||||
// Start component / Subscribe to channel topics
|
// Start component / Subscribe to channel topics
|
||||||
discovered.start(connection, scheduler, 0).exceptionally(e -> {
|
discovered.start(connection, scheduler, 0).exceptionally(e -> {
|
||||||
logger.warn("Failed to start component {}", discovered.getHaID(), e);
|
logger.warn("Failed to start component {}", discovered.getHaID(), e);
|
||||||
@ -392,6 +384,9 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
sortedComponents.stream().map(AbstractComponent::getChannels).flatMap(List::stream)
|
sortedComponents.stream().map(AbstractComponent::getChannels).flatMap(List::stream)
|
||||||
.forEach(c -> thingBuilder.withChannel(c));
|
.forEach(c -> thingBuilder.withChannel(c));
|
||||||
|
|
||||||
|
channelStates.clear();
|
||||||
|
sortedComponents.forEach(c -> c.getChannelStates(channelStates));
|
||||||
|
|
||||||
updateThing(thingBuilder.build());
|
updateThing(thingBuilder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,4 +417,18 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
properties = state.appendToProperties(properties);
|
properties = state.appendToProperties(properties);
|
||||||
updateProperties(properties);
|
updateProperties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should only be called when it's safe to access haComponents
|
||||||
|
private void addComponent(AbstractComponent component) {
|
||||||
|
AbstractComponent existing = haComponents.get(component.getComponentId());
|
||||||
|
if (existing != null) {
|
||||||
|
// rename the conflict
|
||||||
|
haComponents.remove(existing.getComponentId());
|
||||||
|
existing.resolveConflict();
|
||||||
|
component.resolveConflict();
|
||||||
|
haComponents.put(existing.getComponentId(), existing);
|
||||||
|
}
|
||||||
|
haComponents.put(component.getComponentId(), component);
|
||||||
|
haComponentsByUniqueId.put(component.getUniqueId(), component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||||
|
|
||||||
<config-description uri="channel-type:mqtt:ha-channel">
|
<config-description uri="channel-type:mqtt:ha-channel">
|
||||||
<parameter name="component" type="text" readOnly="true" required="true">
|
<parameter name="component" type="text" readOnly="true">
|
||||||
<label>Component</label>
|
<label>Component</label>
|
||||||
<description>Home Assistant component type (e.g. binary_sensor, switch, light)</description>
|
<description>Home Assistant component type (e.g. binary_sensor, switch, light)</description>
|
||||||
<default></default>
|
<default></default>
|
||||||
@ -15,12 +15,12 @@
|
|||||||
<description>Optional node name of the component</description>
|
<description>Optional node name of the component</description>
|
||||||
<default></default>
|
<default></default>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="objectid" type="text" readOnly="true" required="true">
|
<parameter name="objectid" type="text" readOnly="true">
|
||||||
<label>Object ID</label>
|
<label>Object ID</label>
|
||||||
<description>Object ID of the component</description>
|
<description>Object ID of the component</description>
|
||||||
<default></default>
|
<default></default>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="config" type="text" readOnly="true" required="true">
|
<parameter name="config" type="text" readOnly="true">
|
||||||
<label>JSON Configuration</label>
|
<label>JSON Configuration</label>
|
||||||
<description>The JSON configuration string received by the component via MQTT.</description>
|
<description>The JSON configuration string received by the component via MQTT.</description>
|
||||||
<default></default>
|
<default></default>
|
||||||
|
@ -65,7 +65,7 @@ public class BinarySensorTests extends AbstractComponentTests {
|
|||||||
|
|
||||||
assertThat(component.channels.size(), is(1));
|
assertThat(component.channels.size(), is(1));
|
||||||
assertThat(component.getName(), is("onoffsensor"));
|
assertThat(component.getName(), is("onoffsensor"));
|
||||||
assertThat(component.getGroupId(), is("sn1"));
|
assertThat(component.getComponentId(), is("sn1"));
|
||||||
|
|
||||||
assertChannel(component, BinarySensor.SENSOR_CHANNEL_ID, "zigbee2mqtt/sensor/state", "", "onoffsensor",
|
assertChannel(component, BinarySensor.SENSOR_CHANNEL_ID, "zigbee2mqtt/sensor/state", "", "onoffsensor",
|
||||||
OnOffValue.class);
|
OnOffValue.class);
|
||||||
|
@ -65,7 +65,7 @@ public class SensorTests extends AbstractComponentTests {
|
|||||||
|
|
||||||
assertThat(component.channels.size(), is(1));
|
assertThat(component.channels.size(), is(1));
|
||||||
assertThat(component.getName(), is("sensor1"));
|
assertThat(component.getName(), is("sensor1"));
|
||||||
assertThat(component.getGroupId(), is("sn1"));
|
assertThat(component.getComponentId(), is("sn1"));
|
||||||
|
|
||||||
assertChannel(component, Sensor.SENSOR_CHANNEL_ID, "zigbee2mqtt/sensor/state", "", "sensor1",
|
assertChannel(component, Sensor.SENSOR_CHANNEL_ID, "zigbee2mqtt/sensor/state", "", "sensor1",
|
||||||
NumberValue.class);
|
NumberValue.class);
|
||||||
|
@ -155,7 +155,7 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
|||||||
// and add the types to the channelTypeProvider, like in the real Thing handler.
|
// and add the types to the channelTypeProvider, like in the real Thing handler.
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
ComponentDiscovered cd = (haID, c) -> {
|
ComponentDiscovered cd = (haID, c) -> {
|
||||||
haComponents.put(c.getGroupId(), c);
|
haComponents.put(c.getComponentId(), c);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -174,11 +174,10 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
|||||||
assertNull(failure);
|
assertNull(failure);
|
||||||
assertThat(haComponents.size(), is(1));
|
assertThat(haComponents.size(), is(1));
|
||||||
|
|
||||||
String channelGroupId = "switch_" + ThingChannelConstants.TEST_HOME_ASSISTANT_THING.getId();
|
String componentId = ThingChannelConstants.TEST_HOME_ASSISTANT_THING.getId();
|
||||||
String channelId = Switch.SWITCH_CHANNEL_ID;
|
String channelId = Switch.SWITCH_CHANNEL_ID;
|
||||||
|
|
||||||
State value = haComponents.get(channelGroupId).getChannel(channelGroupId).getState().getCache()
|
State value = haComponents.get(componentId).getChannel(channelId).getState().getCache().getChannelState();
|
||||||
.getChannelState();
|
|
||||||
assertThat(value, is(UnDefType.UNDEF));
|
assertThat(value, is(UnDefType.UNDEF));
|
||||||
|
|
||||||
haComponents.values().stream().map(e -> e.start(haConnection, scheduler, 100))
|
haComponents.values().stream().map(e -> e.start(haConnection, scheduler, 100))
|
||||||
@ -191,7 +190,7 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
|||||||
verify(channelStateUpdateListener, timeout(4000).times(1)).updateChannelState(any(), any());
|
verify(channelStateUpdateListener, timeout(4000).times(1)).updateChannelState(any(), any());
|
||||||
|
|
||||||
// Value should be ON now.
|
// Value should be ON now.
|
||||||
value = haComponents.get(channelGroupId).getChannel(channelGroupId).getState().getCache().getChannelState();
|
value = haComponents.get(componentId).getChannel(channelId).getState().getCache().getChannelState();
|
||||||
assertThat(value, is(OnOffType.ON));
|
assertThat(value, is(OnOffType.ON));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user