mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[mqtt.homie] build a per-thing thing type (#15893)
* [mqtt.homie] build a per-thing thing type Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
parent
7d8a1b17f2
commit
d90a4a1ca2
@ -22,7 +22,9 @@ import org.openhab.binding.mqtt.generic.internal.MqttThingHandlerFactory;
|
|||||||
import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler;
|
import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
|
||||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||||
|
import org.openhab.core.types.CommandDescription;
|
||||||
import org.openhab.core.types.StateDescription;
|
import org.openhab.core.types.StateDescription;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -37,11 +39,14 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* @author David Graeff - Initial contribution
|
* @author David Graeff - Initial contribution
|
||||||
*/
|
*/
|
||||||
@Component(service = { DynamicStateDescriptionProvider.class, MqttChannelStateDescriptionProvider.class })
|
@Component(service = { DynamicStateDescriptionProvider.class, DynamicCommandDescriptionProvider.class,
|
||||||
|
MqttChannelStateDescriptionProvider.class })
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MqttChannelStateDescriptionProvider implements DynamicStateDescriptionProvider {
|
public class MqttChannelStateDescriptionProvider
|
||||||
|
implements DynamicStateDescriptionProvider, DynamicCommandDescriptionProvider {
|
||||||
|
|
||||||
private final Map<ChannelUID, StateDescription> descriptions = new ConcurrentHashMap<>();
|
private final Map<ChannelUID, StateDescription> stateDescriptions = new ConcurrentHashMap<>();
|
||||||
|
private final Map<ChannelUID, CommandDescription> commandDescriptions = new ConcurrentHashMap<>();
|
||||||
private final Logger logger = LoggerFactory.getLogger(MqttChannelStateDescriptionProvider.class);
|
private final Logger logger = LoggerFactory.getLogger(MqttChannelStateDescriptionProvider.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,33 +58,55 @@ public class MqttChannelStateDescriptionProvider implements DynamicStateDescript
|
|||||||
*/
|
*/
|
||||||
public void setDescription(ChannelUID channelUID, StateDescription description) {
|
public void setDescription(ChannelUID channelUID, StateDescription description) {
|
||||||
logger.debug("Adding state description for channel {}", channelUID);
|
logger.debug("Adding state description for channel {}", channelUID);
|
||||||
descriptions.put(channelUID, description);
|
stateDescriptions.put(channelUID, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a command description for a channel.
|
||||||
|
* A previous description, if existed, will be replaced.
|
||||||
|
*
|
||||||
|
* @param channelUID channel UID
|
||||||
|
* @param description command description for the channel
|
||||||
|
*/
|
||||||
|
public void setDescription(ChannelUID channelUID, CommandDescription description) {
|
||||||
|
logger.debug("Adding state description for channel {}", channelUID);
|
||||||
|
commandDescriptions.put(channelUID, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all registered state descriptions
|
* Clear all registered state descriptions
|
||||||
*/
|
*/
|
||||||
public void removeAllDescriptions() {
|
public void removeAllDescriptions() {
|
||||||
logger.debug("Removing all state descriptions");
|
logger.debug("Removing all descriptions");
|
||||||
descriptions.clear();
|
stateDescriptions.clear();
|
||||||
|
commandDescriptions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable StateDescription getStateDescription(Channel channel,
|
public @Nullable StateDescription getStateDescription(Channel channel,
|
||||||
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
|
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
|
||||||
StateDescription description = descriptions.get(channel.getUID());
|
StateDescription description = stateDescriptions.get(channel.getUID());
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
logger.trace("Providing state description for channel {}", channel.getUID());
|
logger.trace("Providing state description for channel {}", channel.getUID());
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable CommandDescription getCommandDescription(Channel channel,
|
||||||
|
@Nullable CommandDescription originalCommandDescription, @Nullable Locale locale) {
|
||||||
|
CommandDescription description = commandDescriptions.get(channel.getUID());
|
||||||
|
logger.trace("Providing command description for channel {}", channel.getUID());
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given channel state description.
|
* Removes the given channel description.
|
||||||
*
|
*
|
||||||
* @param channel The channel
|
* @param channel The channel
|
||||||
*/
|
*/
|
||||||
public void remove(ChannelUID channel) {
|
public void remove(ChannelUID channel) {
|
||||||
descriptions.remove(channel);
|
stateDescriptions.remove(channel);
|
||||||
|
commandDescriptions.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,15 @@ public class MqttBindingConstants {
|
|||||||
// List of all Thing Type UIDs
|
// List of all Thing Type UIDs
|
||||||
public static final ThingTypeUID HOMIE300_MQTT_THING = new ThingTypeUID(BINDING_ID, "homie300");
|
public static final ThingTypeUID HOMIE300_MQTT_THING = new ThingTypeUID(BINDING_ID, "homie300");
|
||||||
|
|
||||||
public static final String CONFIG_HOMIE_CHANNEL = "channel-type:mqtt:homie-channel";
|
public static final String CHANNEL_TYPE_HOMIE_PREFIX = "homie-";
|
||||||
|
public static final String CHANNEL_TYPE_HOMIE_STRING = "homie-string";
|
||||||
|
public static final String CHANNEL_TYPE_HOMIE_TRIGGER = "homie-trigger";
|
||||||
|
|
||||||
|
public static final String CHANNEL_PROPERTY_DATATYPE = "datatype";
|
||||||
|
public static final String CHANNEL_PROPERTY_SETTABLE = "settable";
|
||||||
|
public static final String CHANNEL_PROPERTY_RETAINED = "retained";
|
||||||
|
public static final String CHANNEL_PROPERTY_FORMAT = "format";
|
||||||
|
public static final String CHANNEL_PROPERTY_UNIT = "unit";
|
||||||
|
|
||||||
public static final String HOMIE_PROPERTY_VERSION = "homieversion";
|
public static final String HOMIE_PROPERTY_VERSION = "homieversion";
|
||||||
public static final String HOMIE_PROPERTY_HEARTBEAT_INTERVAL = "heartbeat_interval";
|
public static final String HOMIE_PROPERTY_HEARTBEAT_INTERVAL = "heartbeat_interval";
|
||||||
|
@ -16,6 +16,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
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.MqttChannelStateDescriptionProvider;
|
||||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||||
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
|
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
|
||||||
@ -24,12 +25,11 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
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.TransformationHelper;
|
||||||
import org.openhab.core.transform.TransformationService;
|
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.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Deactivate;
|
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,43 +41,40 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
@Component(service = ThingHandlerFactory.class)
|
@Component(service = ThingHandlerFactory.class)
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
|
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
|
||||||
private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider;
|
private final MqttChannelTypeProvider typeProvider;
|
||||||
|
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||||
|
private final ChannelTypeRegistry channelTypeRegistry;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public MqttThingHandlerFactory(final @Reference MqttChannelTypeProvider typeProvider,
|
||||||
|
final @Reference MqttChannelStateDescriptionProvider stateDescriptionProvider,
|
||||||
|
final @Reference ChannelTypeRegistry channelTypeRegistry) {
|
||||||
|
this.typeProvider = typeProvider;
|
||||||
|
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||||
|
this.channelTypeRegistry = channelTypeRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
|
||||||
.of(MqttBindingConstants.HOMIE300_MQTT_THING);
|
.of(MqttBindingConstants.HOMIE300_MQTT_THING);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || isHomieDynamicType(thingTypeUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Activate
|
private boolean isHomieDynamicType(ThingTypeUID thingTypeUID) {
|
||||||
@Override
|
return MqttBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())
|
||||||
protected void activate(ComponentContext componentContext) {
|
&& thingTypeUID.getId().startsWith(MqttBindingConstants.HOMIE300_MQTT_THING.getId());
|
||||||
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
|
@Override
|
||||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
if (thingTypeUID.equals(MqttBindingConstants.HOMIE300_MQTT_THING)) {
|
if (supportsThingType(thingTypeUID)) {
|
||||||
return new HomieThingHandler(thing, typeProvider, MqttBindingConstants.HOMIE_DEVICE_TIMEOUT_MS,
|
return new HomieThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
|
||||||
MqttBindingConstants.HOMIE_SUBSCRIBE_TIMEOUT_MS, MqttBindingConstants.HOMIE_ATTRIBUTE_TIMEOUT_MS);
|
MqttBindingConstants.HOMIE_DEVICE_TIMEOUT_MS, MqttBindingConstants.HOMIE_SUBSCRIBE_TIMEOUT_MS,
|
||||||
|
MqttBindingConstants.HOMIE_ATTRIBUTE_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
package org.openhab.binding.mqtt.homie.internal.handler;
|
package org.openhab.binding.mqtt.homie.internal.handler;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
@ -23,6 +24,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.AbstractMQTTThingHandler;
|
import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler;
|
||||||
import org.openhab.binding.mqtt.generic.ChannelState;
|
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||||
|
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
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.homie.generic.internal.MqttBindingConstants;
|
import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants;
|
||||||
@ -39,6 +41,12 @@ import org.openhab.core.thing.ChannelUID;
|
|||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupDefinition;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||||
|
import org.openhab.core.thing.type.ThingType;
|
||||||
|
import org.openhab.core.types.CommandDescription;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -53,6 +61,8 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
private final Logger logger = LoggerFactory.getLogger(HomieThingHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(HomieThingHandler.class);
|
||||||
protected Device device;
|
protected Device device;
|
||||||
protected final MqttChannelTypeProvider channelTypeProvider;
|
protected final MqttChannelTypeProvider channelTypeProvider;
|
||||||
|
protected final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||||
|
protected final ChannelTypeRegistry channelTypeRegistry;
|
||||||
/** The timeout per attribute field subscription */
|
/** The timeout per attribute field subscription */
|
||||||
protected final int attributeReceiveTimeout;
|
protected final int attributeReceiveTimeout;
|
||||||
protected final int subscribeTimeout;
|
protected final int subscribeTimeout;
|
||||||
@ -67,16 +77,21 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
*
|
*
|
||||||
* @param thing The thing of this handler
|
* @param thing The thing of this handler
|
||||||
* @param channelTypeProvider A channel type provider
|
* @param channelTypeProvider A channel type provider
|
||||||
|
* @param stateDescriptionProvider A state description provider
|
||||||
|
* @param channelTypeRegistry The channel type registry
|
||||||
* @param deviceTimeout Timeout for the entire device subscription. In milliseconds.
|
* @param deviceTimeout Timeout for the entire device subscription. In milliseconds.
|
||||||
* @param subscribeTimeout Timeout for an entire attribute class subscription and receive. In milliseconds.
|
* @param subscribeTimeout Timeout for an entire attribute class subscription and receive. In milliseconds.
|
||||||
* Even a slow remote device will publish a full node or property within 100ms.
|
* Even a slow remote device will publish a full node or property within 100ms.
|
||||||
* @param attributeReceiveTimeout The timeout per attribute field subscription. In milliseconds.
|
* @param attributeReceiveTimeout The timeout per attribute field subscription. In milliseconds.
|
||||||
* One attribute subscription and receiving should not take longer than 50ms.
|
* One attribute subscription and receiving should not take longer than 50ms.
|
||||||
*/
|
*/
|
||||||
public HomieThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider, int deviceTimeout,
|
public HomieThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
|
||||||
int subscribeTimeout, int attributeReceiveTimeout) {
|
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
|
||||||
|
int deviceTimeout, int subscribeTimeout, int attributeReceiveTimeout) {
|
||||||
super(thing, deviceTimeout);
|
super(thing, deviceTimeout);
|
||||||
this.channelTypeProvider = channelTypeProvider;
|
this.channelTypeProvider = channelTypeProvider;
|
||||||
|
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||||
|
this.channelTypeRegistry = channelTypeRegistry;
|
||||||
this.deviceTimeout = deviceTimeout;
|
this.deviceTimeout = deviceTimeout;
|
||||||
this.subscribeTimeout = subscribeTimeout;
|
this.subscribeTimeout = subscribeTimeout;
|
||||||
this.attributeReceiveTimeout = attributeReceiveTimeout;
|
this.attributeReceiveTimeout = attributeReceiveTimeout;
|
||||||
@ -105,6 +120,17 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
device.initialize(config.basetopic, config.deviceid, thing.getChannels());
|
device.initialize(config.basetopic, config.deviceid, thing.getChannels());
|
||||||
|
|
||||||
|
updateThingType();
|
||||||
|
if (getThing().getThingTypeUID().equals(MqttBindingConstants.HOMIE300_MQTT_THING)) {
|
||||||
|
logger.debug("Migrating Homie thing {} from generic type to dynamic type {}", getThing().getUID(),
|
||||||
|
device.thingTypeUID);
|
||||||
|
changeThingType(device.thingTypeUID, getConfig());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
updateChannels();
|
||||||
|
}
|
||||||
|
|
||||||
super.initialize();
|
super.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +169,7 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
}
|
}
|
||||||
delayedProcessing.join();
|
delayedProcessing.join();
|
||||||
device.stop();
|
device.stop();
|
||||||
|
channelTypeProvider.removeThingType(device.thingTypeUID);
|
||||||
super.stop();
|
super.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +222,7 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyRemoved(Property property) {
|
public void propertyRemoved(Property property) {
|
||||||
channelTypeProvider.removeChannelType(property.channelTypeUID);
|
stateDescriptionProvider.remove(property.getChannelUID());
|
||||||
delayedProcessing.accept(property);
|
delayedProcessing.accept(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +234,16 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyAddedOrChanged(Property property) {
|
public void propertyAddedOrChanged(Property property) {
|
||||||
channelTypeProvider.setChannelType(property.channelTypeUID, property.getType());
|
ChannelUID channelUID = property.getChannelUID();
|
||||||
|
stateDescriptionProvider.remove(channelUID);
|
||||||
|
StateDescription stateDescription = property.getStateDescription();
|
||||||
|
if (stateDescription != null) {
|
||||||
|
stateDescriptionProvider.setDescription(channelUID, stateDescription);
|
||||||
|
}
|
||||||
|
CommandDescription commandDescription = property.getCommandDescription();
|
||||||
|
if (commandDescription != null) {
|
||||||
|
stateDescriptionProvider.setDescription(channelUID, commandDescription);
|
||||||
|
}
|
||||||
delayedProcessing.accept(property);
|
delayedProcessing.accept(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,10 +256,9 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
if (!device.isInitialized()) {
|
if (!device.isInitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Channel> channels = device.nodes().stream().flatMap(n -> n.properties.stream()).map(Property::getChannel)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
updateThing(editThing().withChannels(channels).build());
|
|
||||||
updateProperty(MqttBindingConstants.HOMIE_PROPERTY_VERSION, device.attributes.homie);
|
updateProperty(MqttBindingConstants.HOMIE_PROPERTY_VERSION, device.attributes.homie);
|
||||||
|
updateThingType();
|
||||||
|
updateChannels();
|
||||||
final MqttBrokerConnection connection = this.connection;
|
final MqttBrokerConnection connection = this.connection;
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
device.startChannels(connection, scheduler, attributeReceiveTimeout, this).thenRun(() -> {
|
device.startChannels(connection, scheduler, attributeReceiveTimeout, this).thenRun(() -> {
|
||||||
@ -249,4 +284,31 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) {
|
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) {
|
||||||
// not used here
|
// not used here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateThingType() {
|
||||||
|
// Make sure any dynamic channel types exist (i.e. ones created for a number channel with a specific dimension)
|
||||||
|
device.nodes.stream().flatMap(n -> n.properties.stream()).map(Property::getChannelType).filter(Objects::nonNull)
|
||||||
|
.forEach(ct -> channelTypeProvider.setChannelType(ct.getUID(), ct));
|
||||||
|
|
||||||
|
// if this is a dynamic type, then we update the type
|
||||||
|
ThingTypeUID typeID = device.thingTypeUID;
|
||||||
|
if (!MqttBindingConstants.HOMIE300_MQTT_THING.equals(typeID)) {
|
||||||
|
device.nodes.stream()
|
||||||
|
.forEach(n -> channelTypeProvider.setChannelGroupType(n.channelGroupTypeUID, n.type()));
|
||||||
|
|
||||||
|
List<ChannelGroupDefinition> groupDefs = device.nodes().stream().map(Node::getChannelGroupDefinition)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
var builder = channelTypeProvider.derive(typeID, MqttBindingConstants.HOMIE300_MQTT_THING)
|
||||||
|
.withChannelGroupDefinitions(groupDefs);
|
||||||
|
ThingType thingType = builder.build();
|
||||||
|
|
||||||
|
channelTypeProvider.setThingType(typeID, thingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateChannels() {
|
||||||
|
List<Channel> channels = device.nodes().stream().flatMap(n -> n.properties.stream())
|
||||||
|
.map(p -> p.getChannel(channelTypeRegistry)).collect(Collectors.toList());
|
||||||
|
updateThing(editThing().withChannels(channels).build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,14 @@ 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.ChannelConfig;
|
|
||||||
import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass;
|
import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass;
|
||||||
import org.openhab.binding.mqtt.generic.tools.ChildMap;
|
import org.openhab.binding.mqtt.generic.tools.ChildMap;
|
||||||
|
import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants;
|
||||||
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
|
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
|
||||||
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.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
import org.openhab.core.util.UIDUtils;
|
import org.openhab.core.util.UIDUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -61,6 +62,7 @@ public class Device implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
|
|
||||||
// The corresponding ThingUID and callback of this device object
|
// The corresponding ThingUID and callback of this device object
|
||||||
public final ThingUID thingUID;
|
public final ThingUID thingUID;
|
||||||
|
public ThingTypeUID thingTypeUID = MqttBindingConstants.HOMIE300_MQTT_THING;
|
||||||
private final DeviceCallback callback;
|
private final DeviceCallback callback;
|
||||||
|
|
||||||
// Unique identifier and topic
|
// Unique identifier and topic
|
||||||
@ -76,10 +78,7 @@ public class Device implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
* @param attributes The device attributes object
|
* @param attributes The device attributes object
|
||||||
*/
|
*/
|
||||||
public Device(ThingUID thingUID, DeviceCallback callback, DeviceAttributes attributes) {
|
public Device(ThingUID thingUID, DeviceCallback callback, DeviceAttributes attributes) {
|
||||||
this.thingUID = thingUID;
|
this(thingUID, callback, attributes, new ChildMap<>());
|
||||||
this.callback = callback;
|
|
||||||
this.attributes = attributes;
|
|
||||||
this.nodes = new ChildMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,13 +203,11 @@ public class Device implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
public void initialize(String baseTopic, String deviceID, List<Channel> channels) {
|
public void initialize(String baseTopic, String deviceID, List<Channel> channels) {
|
||||||
this.topic = baseTopic + "/" + deviceID;
|
this.topic = baseTopic + "/" + deviceID;
|
||||||
this.deviceID = deviceID;
|
this.deviceID = deviceID;
|
||||||
|
this.thingTypeUID = new ThingTypeUID(MqttBindingConstants.BINDING_ID,
|
||||||
|
MqttBindingConstants.HOMIE300_MQTT_THING.getId() + "_" + UIDUtils.encode(topic));
|
||||||
|
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
for (Channel channel : channels) {
|
for (Channel channel : channels) {
|
||||||
final ChannelConfig channelConfig = channel.getConfiguration().as(ChannelConfig.class);
|
|
||||||
if (!channelConfig.commandTopic.isEmpty() && !channelConfig.retained) {
|
|
||||||
logger.warn("Channel {} in device {} is missing the 'retained' flag. Check your configuration.",
|
|
||||||
channel.getUID(), deviceID);
|
|
||||||
}
|
|
||||||
final String channelGroupId = channel.getUID().getGroupId();
|
final String channelGroupId = channel.getUID().getGroupId();
|
||||||
if (channelGroupId == null) {
|
if (channelGroupId == null) {
|
||||||
continue;
|
continue;
|
||||||
@ -223,9 +220,43 @@ public class Device implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
node.nodeRestoredFromConfig();
|
node.nodeRestoredFromConfig();
|
||||||
nodes.put(nodeID, node);
|
nodes.put(nodeID, node);
|
||||||
}
|
}
|
||||||
// Restores the properties attribute object via the channels configuration.
|
// Restores the property's attributes object via the channel's config and properties.
|
||||||
Property property = node.createProperty(propertyID,
|
// (config is only for backwards compatibility before properties were used)
|
||||||
channel.getConfiguration().as(PropertyAttributes.class));
|
var channelConfig = channel.getConfiguration();
|
||||||
|
PropertyAttributes attributes = channelConfig.as(PropertyAttributes.class);
|
||||||
|
|
||||||
|
var channelProperties = channel.getProperties();
|
||||||
|
String channelId = channel.getChannelTypeUID().getId();
|
||||||
|
|
||||||
|
String datatype = channelProperties.get(MqttBindingConstants.CHANNEL_PROPERTY_DATATYPE);
|
||||||
|
if (datatype != null) {
|
||||||
|
attributes.datatype = PropertyAttributes.DataTypeEnum.valueOf(datatype);
|
||||||
|
} else if (channelId.startsWith(MqttBindingConstants.CHANNEL_TYPE_HOMIE_PREFIX)) {
|
||||||
|
attributes.datatype = PropertyAttributes.DataTypeEnum
|
||||||
|
.valueOf(channelId.substring(MqttBindingConstants.CHANNEL_TYPE_HOMIE_PREFIX.length()) + "_");
|
||||||
|
}
|
||||||
|
String label = channel.getLabel();
|
||||||
|
if (label != null) {
|
||||||
|
attributes.name = label;
|
||||||
|
}
|
||||||
|
String settable = channelProperties.get(MqttBindingConstants.CHANNEL_PROPERTY_SETTABLE);
|
||||||
|
if (settable != null) {
|
||||||
|
attributes.settable = Boolean.valueOf(settable);
|
||||||
|
}
|
||||||
|
String retained = channelProperties.get(MqttBindingConstants.CHANNEL_PROPERTY_RETAINED);
|
||||||
|
if (retained != null) {
|
||||||
|
attributes.retained = Boolean.valueOf(retained);
|
||||||
|
}
|
||||||
|
String unit = channelProperties.get(MqttBindingConstants.CHANNEL_PROPERTY_UNIT);
|
||||||
|
if (unit != null) {
|
||||||
|
attributes.unit = unit;
|
||||||
|
}
|
||||||
|
String format = channelProperties.get(MqttBindingConstants.CHANNEL_PROPERTY_FORMAT);
|
||||||
|
if (format != null) {
|
||||||
|
attributes.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
Property property = node.createProperty(propertyID, attributes);
|
||||||
property.attributesReceived();
|
property.attributesReceived();
|
||||||
|
|
||||||
node.properties.put(propertyID, property);
|
node.properties.put(propertyID, property);
|
||||||
|
@ -29,7 +29,7 @@ import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
|||||||
import org.openhab.core.thing.ChannelGroupUID;
|
import org.openhab.core.thing.ChannelGroupUID;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
import org.openhab.core.thing.type.ChannelDefinition;
|
import org.openhab.core.thing.type.ChannelDefinition;
|
||||||
import org.openhab.core.thing.type.ChannelDefinitionBuilder;
|
import org.openhab.core.thing.type.ChannelGroupDefinition;
|
||||||
import org.openhab.core.thing.type.ChannelGroupType;
|
import org.openhab.core.thing.type.ChannelGroupType;
|
||||||
import org.openhab.core.thing.type.ChannelGroupTypeBuilder;
|
import org.openhab.core.thing.type.ChannelGroupTypeBuilder;
|
||||||
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||||
@ -101,6 +101,7 @@ public class Node implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
|
|
||||||
public void nodeRestoredFromConfig() {
|
public void nodeRestoredFromConfig() {
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
attributes.name = nodeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,12 +119,15 @@ public class Node implements AbstractMqttAttributeClass.AttributeChanged {
|
|||||||
*/
|
*/
|
||||||
public ChannelGroupType type() {
|
public ChannelGroupType type() {
|
||||||
final List<ChannelDefinition> channelDefinitions = properties.stream()
|
final List<ChannelDefinition> channelDefinitions = properties.stream()
|
||||||
.map(c -> new ChannelDefinitionBuilder(c.propertyID, c.channelTypeUID).build())
|
.map(p -> Objects.requireNonNull(p.getChannelDefinition())).collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, attributes.name)
|
return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, attributes.name)
|
||||||
.withChannelDefinitions(channelDefinitions).build();
|
.withChannelDefinitions(channelDefinitions).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChannelGroupDefinition getChannelGroupDefinition() {
|
||||||
|
return new ChannelGroupDefinition(channelGroupUID.getId(), channelGroupTypeUID, attributes.name, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the channel group UID.
|
* Return the channel group UID.
|
||||||
*/
|
*/
|
||||||
|
@ -14,14 +14,18 @@ package org.openhab.binding.mqtt.homie.internal.homie300;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.measure.Unit;
|
||||||
|
|
||||||
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.ChannelConfigBuilder;
|
import org.openhab.binding.mqtt.generic.ChannelConfigBuilder;
|
||||||
@ -37,16 +41,21 @@ import org.openhab.binding.mqtt.generic.values.TextValue;
|
|||||||
import org.openhab.binding.mqtt.generic.values.Value;
|
import org.openhab.binding.mqtt.generic.values.Value;
|
||||||
import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants;
|
import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants;
|
||||||
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum;
|
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum;
|
||||||
import org.openhab.core.config.core.Configuration;
|
|
||||||
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.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.CommonTriggerEvents;
|
||||||
import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
|
import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
|
||||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||||
import org.openhab.core.thing.type.AutoUpdatePolicy;
|
import org.openhab.core.thing.type.AutoUpdatePolicy;
|
||||||
|
import org.openhab.core.thing.type.ChannelDefinition;
|
||||||
|
import org.openhab.core.thing.type.ChannelDefinitionBuilder;
|
||||||
import org.openhab.core.thing.type.ChannelType;
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
import org.openhab.core.thing.type.ChannelTypeBuilder;
|
import org.openhab.core.thing.type.ChannelTypeBuilder;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||||
|
import org.openhab.core.types.CommandDescription;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
import org.openhab.core.types.util.UnitUtils;
|
import org.openhab.core.types.util.UnitUtils;
|
||||||
import org.openhab.core.util.UIDUtils;
|
import org.openhab.core.util.UIDUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -67,9 +76,11 @@ public class Property implements AttributeChanged {
|
|||||||
// Runtime state
|
// Runtime state
|
||||||
protected @Nullable ChannelState channelState;
|
protected @Nullable ChannelState channelState;
|
||||||
public final ChannelUID channelUID;
|
public final ChannelUID channelUID;
|
||||||
public final ChannelTypeUID channelTypeUID;
|
public ChannelTypeUID channelTypeUID;
|
||||||
private ChannelType type;
|
private @Nullable ChannelType channelType = null;
|
||||||
private Channel channel;
|
private @Nullable ChannelDefinition channelDefinition = null;
|
||||||
|
private @Nullable StateDescription stateDescription = null;
|
||||||
|
private @Nullable CommandDescription commandDescription = null;
|
||||||
private final String topic;
|
private final String topic;
|
||||||
private final DeviceCallback callback;
|
private final DeviceCallback callback;
|
||||||
protected boolean initialized = false;
|
protected boolean initialized = false;
|
||||||
@ -89,9 +100,8 @@ public class Property implements AttributeChanged {
|
|||||||
this.parentNode = node;
|
this.parentNode = node;
|
||||||
this.propertyID = propertyID;
|
this.propertyID = propertyID;
|
||||||
channelUID = new ChannelUID(node.uid(), UIDUtils.encode(propertyID));
|
channelUID = new ChannelUID(node.uid(), UIDUtils.encode(propertyID));
|
||||||
channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID, UIDUtils.encode(this.topic));
|
channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID,
|
||||||
type = ChannelTypeBuilder.trigger(channelTypeUID, "dummy").build(); // Dummy value
|
MqttBindingConstants.CHANNEL_TYPE_HOMIE_STRING);
|
||||||
channel = ChannelBuilder.create(channelUID, "dummy").build();// Dummy value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,52 +138,11 @@ public class Property implements AttributeChanged {
|
|||||||
* ChannelState are determined.
|
* ChannelState are determined.
|
||||||
*/
|
*/
|
||||||
public void attributesReceived() {
|
public void attributesReceived() {
|
||||||
createChannelFromAttribute();
|
createChannelTypeFromAttributes();
|
||||||
callback.propertyAddedOrChanged(this);
|
callback.propertyAddedOrChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void createChannelTypeFromAttributes() {
|
||||||
* Creates the ChannelType of the Homie property.
|
|
||||||
*
|
|
||||||
* @param attributes Attributes of the property.
|
|
||||||
* @param channelState ChannelState of the property.
|
|
||||||
*
|
|
||||||
* @return Returns the ChannelType to be used to build the Channel.
|
|
||||||
*/
|
|
||||||
private ChannelType createChannelType(PropertyAttributes attributes, ChannelState channelState) {
|
|
||||||
// Retained property -> State channel
|
|
||||||
if (attributes.retained) {
|
|
||||||
return ChannelTypeBuilder.state(channelTypeUID, attributes.name, channelState.getItemType())
|
|
||||||
.withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HOMIE_CHANNEL))
|
|
||||||
.withStateDescriptionFragment(
|
|
||||||
channelState.getCache().createStateDescription(!attributes.settable).build())
|
|
||||||
.build();
|
|
||||||
} else {
|
|
||||||
// Non-retained and settable property -> State channel
|
|
||||||
if (attributes.settable) {
|
|
||||||
return ChannelTypeBuilder.state(channelTypeUID, attributes.name, channelState.getItemType())
|
|
||||||
.withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HOMIE_CHANNEL))
|
|
||||||
.withCommandDescription(channelState.getCache().createCommandDescription().build())
|
|
||||||
.withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build();
|
|
||||||
}
|
|
||||||
// Non-retained and non settable property -> Trigger channel
|
|
||||||
if (attributes.datatype.equals(DataTypeEnum.enum_)) {
|
|
||||||
if (attributes.format.contains("PRESSED") && attributes.format.contains("RELEASED")) {
|
|
||||||
return DefaultSystemChannelTypeProvider.SYSTEM_RAWBUTTON;
|
|
||||||
} else if (attributes.format.contains("SHORT_PRESSED") && attributes.format.contains("LONG_PRESSED")
|
|
||||||
&& attributes.format.contains("DOUBLE_PRESSED")) {
|
|
||||||
return DefaultSystemChannelTypeProvider.SYSTEM_BUTTON;
|
|
||||||
} else if (attributes.format.contains("DIR1_PRESSED") && attributes.format.contains("DIR1_RELEASED")
|
|
||||||
&& attributes.format.contains("DIR2_PRESSED") && attributes.format.contains("DIR2_RELEASED")) {
|
|
||||||
return DefaultSystemChannelTypeProvider.SYSTEM_RAWROCKER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ChannelTypeBuilder.trigger(channelTypeUID, attributes.name)
|
|
||||||
.withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HOMIE_CHANNEL)).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createChannelFromAttribute() {
|
|
||||||
final String commandTopic = topic + "/set";
|
final String commandTopic = topic + "/set";
|
||||||
final String stateTopic = topic;
|
final String stateTopic = topic;
|
||||||
|
|
||||||
@ -184,6 +153,12 @@ public class Property implements AttributeChanged {
|
|||||||
attributes.name = propertyID;
|
attributes.name = propertyID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Unit<?> unit = UnitUtils.parseUnit(attributes.unit);
|
||||||
|
String dimension = null;
|
||||||
|
if (unit != null) {
|
||||||
|
dimension = UnitUtils.getDimensionName(unit);
|
||||||
|
}
|
||||||
|
|
||||||
switch (attributes.datatype) {
|
switch (attributes.datatype) {
|
||||||
case boolean_:
|
case boolean_:
|
||||||
value = new OnOffValue("true", "false");
|
value = new OnOffValue("true", "false");
|
||||||
@ -218,7 +193,7 @@ public class Property implements AttributeChanged {
|
|||||||
if (attributes.unit.contains("%") && attributes.settable) {
|
if (attributes.unit.contains("%") && attributes.settable) {
|
||||||
value = new PercentageValue(min, max, step, null, null);
|
value = new PercentageValue(min, max, step, null, null);
|
||||||
} else {
|
} else {
|
||||||
value = new NumberValue(min, max, step, UnitUtils.parseUnit(attributes.unit));
|
value = new NumberValue(min, max, step, unit);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case datetime_:
|
case datetime_:
|
||||||
@ -245,12 +220,86 @@ public class Property implements AttributeChanged {
|
|||||||
final ChannelState channelState = new ChannelState(b.build(), channelUID, value, callback);
|
final ChannelState channelState = new ChannelState(b.build(), channelUID, value, callback);
|
||||||
this.channelState = channelState;
|
this.channelState = channelState;
|
||||||
|
|
||||||
final ChannelType type = createChannelType(attributes, channelState);
|
Map<String, String> channelProperties = new HashMap<>();
|
||||||
this.type = type;
|
|
||||||
|
|
||||||
this.channel = ChannelBuilder.create(channelUID, type.getItemType()).withType(type.getUID())
|
if (attributes.settable) {
|
||||||
.withKind(type.getKind()).withLabel(attributes.name)
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_SETTABLE,
|
||||||
.withConfiguration(new Configuration(attributes.asMap())).build();
|
Boolean.toString(attributes.settable));
|
||||||
|
}
|
||||||
|
if (!attributes.retained) {
|
||||||
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_RETAINED,
|
||||||
|
Boolean.toString(attributes.retained));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attributes.format.isEmpty()) {
|
||||||
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_FORMAT, attributes.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channelType = null;
|
||||||
|
if (!attributes.retained && !attributes.settable) {
|
||||||
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_DATATYPE, attributes.datatype.toString());
|
||||||
|
if (attributes.datatype.equals(DataTypeEnum.enum_)) {
|
||||||
|
if (attributes.format.contains(CommonTriggerEvents.PRESSED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.RELEASED)) {
|
||||||
|
this.channelTypeUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_RAWBUTTON;
|
||||||
|
} else if (attributes.format.contains(CommonTriggerEvents.SHORT_PRESSED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.LONG_PRESSED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.DOUBLE_PRESSED)) {
|
||||||
|
this.channelTypeUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_BUTTON;
|
||||||
|
} else if (attributes.format.contains(CommonTriggerEvents.DIR1_PRESSED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.DIR1_RELEASED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.DIR2_PRESSED)
|
||||||
|
&& attributes.format.contains(CommonTriggerEvents.DIR2_RELEASED)) {
|
||||||
|
this.channelTypeUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_RAWROCKER;
|
||||||
|
} else {
|
||||||
|
this.channelTypeUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_TRIGGER;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.channelTypeUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_TRIGGER;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!attributes.unit.isEmpty()) {
|
||||||
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_UNIT, attributes.unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
String channelTypeId;
|
||||||
|
|
||||||
|
if (attributes.datatype.equals(DataTypeEnum.unknown)) {
|
||||||
|
channelTypeId = MqttBindingConstants.CHANNEL_TYPE_HOMIE_STRING;
|
||||||
|
} else if (dimension != null) {
|
||||||
|
channelTypeId = MqttBindingConstants.CHANNEL_TYPE_HOMIE_PREFIX + "number-" + dimension.toLowerCase();
|
||||||
|
channelProperties.put(MqttBindingConstants.CHANNEL_PROPERTY_DATATYPE, attributes.datatype.toString());
|
||||||
|
} else {
|
||||||
|
channelTypeId = MqttBindingConstants.CHANNEL_TYPE_HOMIE_PREFIX + attributes.datatype.toString();
|
||||||
|
channelTypeId = channelTypeId.substring(0, channelTypeId.length() - 1);
|
||||||
|
}
|
||||||
|
this.channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID, channelTypeId);
|
||||||
|
if (dimension != null) {
|
||||||
|
this.channelType = ChannelTypeBuilder.state(channelTypeUID, dimension + " Value", "Number:" + dimension)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.retained) {
|
||||||
|
this.commandDescription = null;
|
||||||
|
this.stateDescription = channelState.getCache().createStateDescription(!attributes.settable).build()
|
||||||
|
.toStateDescription();
|
||||||
|
} else if (attributes.settable) {
|
||||||
|
this.commandDescription = channelState.getCache().createCommandDescription().build();
|
||||||
|
this.stateDescription = null;
|
||||||
|
} else {
|
||||||
|
this.commandDescription = null;
|
||||||
|
this.stateDescription = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new ChannelDefinitionBuilder(UIDUtils.encode(propertyID), channelTypeUID)
|
||||||
|
.withLabel(attributes.name).withProperties(channelProperties);
|
||||||
|
|
||||||
|
if (attributes.settable && !attributes.retained) {
|
||||||
|
builder.withAutoUpdatePolicy(AutoUpdatePolicy.VETO);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channelDefinition = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,8 +315,14 @@ public class Property implements AttributeChanged {
|
|||||||
return attributes.unsubscribe();
|
return attributes.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChannelUID getChannelUID() {
|
||||||
|
return channelUID;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the channelState. You should have called
|
* @return Returns the channelState.
|
||||||
|
*
|
||||||
|
* You should have called
|
||||||
* {@link Property#subscribe(MqttBrokerConnection, ScheduledExecutorService, int)}
|
* {@link Property#subscribe(MqttBrokerConnection, ScheduledExecutorService, int)}
|
||||||
* and waited for the future to complete before calling this Getter.
|
* and waited for the future to complete before calling this Getter.
|
||||||
*/
|
*/
|
||||||
@ -275,6 +330,50 @@ public class Property implements AttributeChanged {
|
|||||||
return channelState;
|
return channelState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the channelType, if a dynamic one is necessary.
|
||||||
|
*
|
||||||
|
* You should have called
|
||||||
|
* {@link Property#subscribe(AbstractMqttAttributeClass, int)}
|
||||||
|
* and waited for the future to complete before calling this Getter.
|
||||||
|
*/
|
||||||
|
public @Nullable ChannelType getChannelType() {
|
||||||
|
return channelType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the ChannelDefinition.
|
||||||
|
*
|
||||||
|
* You should have called
|
||||||
|
* {@link Property#subscribe(AbstractMqttAttributeClass, int)}
|
||||||
|
* and waited for the future to complete before calling this Getter.
|
||||||
|
*/
|
||||||
|
public @Nullable ChannelDefinition getChannelDefinition() {
|
||||||
|
return channelDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the StateDescription.
|
||||||
|
*
|
||||||
|
* You should have called
|
||||||
|
* {@link Property#subscribe(AbstractMqttAttributeClass, int)}
|
||||||
|
* and waited for the future to complete before calling this Getter.
|
||||||
|
*/
|
||||||
|
public @Nullable StateDescription getStateDescription() {
|
||||||
|
return stateDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the CommandDescription.
|
||||||
|
*
|
||||||
|
* You should have called
|
||||||
|
* {@link Property#subscribe(AbstractMqttAttributeClass, int)}
|
||||||
|
* and waited for the future to complete before calling this Getter.
|
||||||
|
*/
|
||||||
|
public @Nullable CommandDescription getCommandDescription() {
|
||||||
|
return commandDescription;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to the state topic on the given connection and informs about updates on the given listener.
|
* Subscribes to the state topic on the given connection and informs about updates on the given listener.
|
||||||
*
|
*
|
||||||
@ -297,19 +396,15 @@ public class Property implements AttributeChanged {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the channel type of this property.
|
* @return Create a channel for this property.
|
||||||
* The type is a dummy only if {@link #channelState} has not been set yet.
|
|
||||||
*/
|
*/
|
||||||
public ChannelType getType() {
|
public Channel getChannel(ChannelTypeRegistry channelTypeRegistry) {
|
||||||
return type;
|
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return ChannelBuilder.create(channelUID, channelType.getItemType()).withType(channelTypeUID)
|
||||||
* @return Returns the channel of this property.
|
.withKind(channelType.getKind()).withLabel(Objects.requireNonNull(channelDefinition.getLabel()))
|
||||||
* The channel is a dummy only if {@link #channelState} has not been set yet.
|
.withProperties(channelDefinition.getProperties())
|
||||||
*/
|
.withAutoUpdatePolicy(channelDefinition.getAutoUpdatePolicy()).build();
|
||||||
public Channel getChannel() {
|
|
||||||
return channel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<config-description:config-descriptions
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
|
||||||
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:homie-channel">
|
|
||||||
<parameter name="unit" type="text">
|
|
||||||
<label>Unit</label>
|
|
||||||
<description>The channels unit</description>
|
|
||||||
<default></default>
|
|
||||||
</parameter>
|
|
||||||
<parameter name="name" type="text">
|
|
||||||
<label>Name</label>
|
|
||||||
<description>The channel name</description>
|
|
||||||
<default></default>
|
|
||||||
</parameter>
|
|
||||||
<parameter name="settable" type="text">
|
|
||||||
<label>Settable</label>
|
|
||||||
<description>Is this channel writable?</description>
|
|
||||||
<default>true</default>
|
|
||||||
</parameter>
|
|
||||||
<parameter name="retained" type="text">
|
|
||||||
<label>Retained</label>
|
|
||||||
<description>If set to false, the resulting channel will be a trigger channel (stateless), useful for non-permanent
|
|
||||||
events. This flag corresponds to the retained option for MQTT publish.</description>
|
|
||||||
<default>true</default>
|
|
||||||
</parameter>
|
|
||||||
<parameter name="format" type="text">
|
|
||||||
<label>Format</label>
|
|
||||||
<description>The output format.</description>
|
|
||||||
<default></default>
|
|
||||||
</parameter>
|
|
||||||
<parameter name="datatype" type="text">
|
|
||||||
<label>Data Type</label>
|
|
||||||
<description>The data type of this channel.</description>
|
|
||||||
<default>unknown</default>
|
|
||||||
<options>
|
|
||||||
<option value="integer_">Integer</option>
|
|
||||||
<option value="float_">Float</option>
|
|
||||||
<option value="boolean_">Boolean</option>
|
|
||||||
<option value="string_">String</option>
|
|
||||||
<option value="enum_">Enumeration</option>
|
|
||||||
<option value="color_">Colour</option>
|
|
||||||
<option value="datetime_">DateTime</option>
|
|
||||||
</options>
|
|
||||||
</parameter>
|
|
||||||
</config-description>
|
|
||||||
</config-description:config-descriptions>
|
|
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="mqtt"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<channel-type id="homie-boolean">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Boolean</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-color">
|
||||||
|
<item-type>Color</item-type>
|
||||||
|
<label>Color Value (Red,Green,Blue)</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-datetime">
|
||||||
|
<item-type>DateTime</item-type>
|
||||||
|
<label>Date/Time Value</label>
|
||||||
|
<description>Current date and/or time</description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-enum">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Enum Value</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-integer">
|
||||||
|
<item-type>Number</item-type>
|
||||||
|
<label>Integer Value</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-float">
|
||||||
|
<item-type>Number</item-type>
|
||||||
|
<label>Float Value</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-string">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Text Value</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="homie-trigger">
|
||||||
|
<kind>trigger</kind>
|
||||||
|
<label>Trigger</label>
|
||||||
|
</channel-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -41,6 +41,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
import org.mockito.junit.jupiter.MockitoSettings;
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
import org.openhab.binding.mqtt.generic.ChannelState;
|
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||||
|
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||||
import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass;
|
import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass;
|
||||||
import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic;
|
import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic;
|
||||||
@ -66,9 +67,13 @@ import org.openhab.core.thing.Thing;
|
|||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
import org.openhab.core.thing.ThingStatusInfo;
|
import org.openhab.core.thing.ThingStatusInfo;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||||
import org.openhab.core.thing.type.ChannelKind;
|
import org.openhab.core.thing.type.ChannelKind;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||||
|
import org.openhab.core.thing.type.ThingTypeBuilder;
|
||||||
import org.openhab.core.thing.type.ThingTypeRegistry;
|
import org.openhab.core.thing.type.ThingTypeRegistry;
|
||||||
import org.openhab.core.types.RefreshType;
|
import org.openhab.core.types.RefreshType;
|
||||||
|
|
||||||
@ -88,11 +93,14 @@ public class HomieThingHandlerTests {
|
|||||||
private @Mock @NonNullByDefault({}) ScheduledExecutorService schedulerMock;
|
private @Mock @NonNullByDefault({}) ScheduledExecutorService schedulerMock;
|
||||||
private @Mock @NonNullByDefault({}) ScheduledFuture<?> scheduledFutureMock;
|
private @Mock @NonNullByDefault({}) ScheduledFuture<?> scheduledFutureMock;
|
||||||
private @Mock @NonNullByDefault({}) ThingTypeRegistry thingTypeRegistryMock;
|
private @Mock @NonNullByDefault({}) ThingTypeRegistry thingTypeRegistryMock;
|
||||||
|
private @Mock @NonNullByDefault({}) ChannelTypeRegistry channelTypeRegistryMock;
|
||||||
|
private @Mock @NonNullByDefault({}) ChannelType channelTypeMock;
|
||||||
|
|
||||||
private @NonNullByDefault({}) Thing thing;
|
private @NonNullByDefault({}) Thing thing;
|
||||||
private @NonNullByDefault({}) HomieThingHandler thingHandler;
|
private @NonNullByDefault({}) HomieThingHandler thingHandler;
|
||||||
|
|
||||||
private final MqttChannelTypeProvider channelTypeProvider = new MqttChannelTypeProvider(thingTypeRegistryMock);
|
private final MqttChannelTypeProvider channelTypeProvider = spy(new MqttChannelTypeProvider(thingTypeRegistryMock));
|
||||||
|
private final MqttChannelStateDescriptionProvider stateDescriptionProvider = new MqttChannelStateDescriptionProvider();
|
||||||
|
|
||||||
private final String deviceID = ThingChannelConstants.TEST_HOMIE_THING.getId();
|
private final String deviceID = ThingChannelConstants.TEST_HOMIE_THING.getId();
|
||||||
private final String deviceTopic = "homie/" + deviceID;
|
private final String deviceTopic = "homie/" + deviceID;
|
||||||
@ -108,8 +116,11 @@ public class HomieThingHandlerTests {
|
|||||||
config.put("basetopic", "homie");
|
config.put("basetopic", "homie");
|
||||||
config.put("deviceid", deviceID);
|
config.put("deviceid", deviceID);
|
||||||
|
|
||||||
thing = ThingBuilder.create(MqttBindingConstants.HOMIE300_MQTT_THING, TEST_HOMIE_THING.getId())
|
ThingTypeUID type = new ThingTypeUID(MqttBindingConstants.BINDING_ID,
|
||||||
.withConfiguration(config).build();
|
MqttBindingConstants.HOMIE300_MQTT_THING.getId() + "_dynamic");
|
||||||
|
doAnswer(i -> ThingTypeBuilder.instance(type, "Homie Thing")).when(channelTypeProvider).derive(any(), any());
|
||||||
|
|
||||||
|
thing = ThingBuilder.create(type, TEST_HOMIE_THING.getId()).withConfiguration(config).build();
|
||||||
thing.setStatusInfo(thingStatus);
|
thing.setStatusInfo(thingStatus);
|
||||||
|
|
||||||
// Return the mocked connection object if the bridge handler is asked for it
|
// Return the mocked connection object if the bridge handler is asked for it
|
||||||
@ -124,7 +135,8 @@ public class HomieThingHandlerTests {
|
|||||||
doReturn(false).when(scheduledFutureMock).isDone();
|
doReturn(false).when(scheduledFutureMock).isDone();
|
||||||
doReturn(scheduledFutureMock).when(schedulerMock).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class));
|
doReturn(scheduledFutureMock).when(schedulerMock).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class));
|
||||||
|
|
||||||
final HomieThingHandler handler = new HomieThingHandler(thing, channelTypeProvider, 1000, 30, 5);
|
final HomieThingHandler handler = new HomieThingHandler(thing, channelTypeProvider, stateDescriptionProvider,
|
||||||
|
channelTypeRegistryMock, 1000, 30, 5);
|
||||||
thingHandler = spy(handler);
|
thingHandler = spy(handler);
|
||||||
thingHandler.setCallback(callbackMock);
|
thingHandler.setCallback(callbackMock);
|
||||||
final Device device = new Device(thing.getUID(), thingHandler, spy(new DeviceAttributes()),
|
final Device device = new Device(thing.getUID(), thingHandler, spy(new DeviceAttributes()),
|
||||||
@ -314,6 +326,10 @@ public class HomieThingHandlerTests {
|
|||||||
thingHandler.device.initialize("homie", "device", new ArrayList<>());
|
thingHandler.device.initialize("homie", "device", new ArrayList<>());
|
||||||
ThingHandlerHelper.setConnection(thingHandler, connectionMock);
|
ThingHandlerHelper.setConnection(thingHandler, connectionMock);
|
||||||
|
|
||||||
|
doReturn("String").when(channelTypeMock).getItemType();
|
||||||
|
doReturn(ChannelKind.STATE).when(channelTypeMock).getKind();
|
||||||
|
doReturn(channelTypeMock).when(channelTypeRegistryMock).getChannelType(any());
|
||||||
|
|
||||||
// Create mocked homie device tree with one node and one property
|
// Create mocked homie device tree with one node and one property
|
||||||
doAnswer(this::createSubscriberAnswer).when(thingHandler.device.attributes).createSubscriber(any(), any(),
|
doAnswer(this::createSubscriberAnswer).when(thingHandler.device.attributes).createSubscriber(any(), any(),
|
||||||
any(), anyBoolean());
|
any(), anyBoolean());
|
||||||
|
@ -281,8 +281,8 @@ public class HomieImplementationTest extends MqttOSGiTest {
|
|||||||
assertThat(property.attributes.format, is("-100:100"));
|
assertThat(property.attributes.format, is("-100:100"));
|
||||||
verify(property).attributesReceived();
|
verify(property).attributesReceived();
|
||||||
assertNotNull(property.getChannelState());
|
assertNotNull(property.getChannelState());
|
||||||
assertThat(property.getType().getState().getMinimum().intValue(), is(-100));
|
assertThat(property.getStateDescription().getMinimum().intValue(), is(-100));
|
||||||
assertThat(property.getType().getState().getMaximum().intValue(), is(100));
|
assertThat(property.getStateDescription().getMaximum().intValue(), is(100));
|
||||||
|
|
||||||
// Check property and property attributes
|
// Check property and property attributes
|
||||||
Property propertyBell = node.properties.get("doorbell");
|
Property propertyBell = node.properties.get("doorbell");
|
||||||
|
Loading…
Reference in New Issue
Block a user