mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[mqtt.homeassistant] Make sensors compliant (#8591)
Signed-off-by: Jochen Klein <git@jochen.susca.de>
This commit is contained in:
parent
3abe27b224
commit
f06068a189
@ -15,6 +15,7 @@ package org.openhab.binding.mqtt.generic;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -355,16 +356,16 @@ public abstract class AbstractMQTTThingHandler extends BaseThingHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void calculateThingStatus() {
|
protected void calculateThingStatus() {
|
||||||
final boolean availabilityTopicsSeen;
|
final Optional<Boolean> availabilityTopicsSeen;
|
||||||
|
|
||||||
if (availabilityStates.isEmpty()) {
|
if (availabilityStates.isEmpty()) {
|
||||||
availabilityTopicsSeen = true;
|
availabilityTopicsSeen = Optional.empty();
|
||||||
} else {
|
} else {
|
||||||
availabilityTopicsSeen = availabilityStates.values().stream().allMatch(
|
availabilityTopicsSeen = Optional.of(availabilityStates.values().stream().allMatch(
|
||||||
c -> c != null && OnOffType.ON.equals(c.getCache().getChannelState().as(OnOffType.class)));
|
c -> c != null && OnOffType.ON.equals(c.getCache().getChannelState().as(OnOffType.class))));
|
||||||
}
|
}
|
||||||
updateThingStatus(messageReceived.get(), availabilityTopicsSeen);
|
updateThingStatus(messageReceived.get(), availabilityTopicsSeen);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void updateThingStatus(boolean messageReceived, boolean availabilityTopicsSeen);
|
protected abstract void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -186,8 +187,8 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateThingStatus(boolean messageReceived, boolean availibilityTopicsSeen) {
|
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availibilityTopicsSeen) {
|
||||||
if (messageReceived || availibilityTopicsSeen) {
|
if (availibilityTopicsSeen.orElse(true)) {
|
||||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
||||||
} else {
|
} else {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.mqtt.homeassistant.internal;
|
package org.openhab.binding.mqtt.homeassistant.internal;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
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;
|
||||||
@ -46,9 +48,10 @@ public class CFactory {
|
|||||||
*/
|
*/
|
||||||
public static @Nullable AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID,
|
public static @Nullable AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID,
|
||||||
String channelConfigurationJSON, ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
|
String channelConfigurationJSON, ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
|
||||||
Gson gson, TransformationServiceProvider transformationServiceProvider) {
|
ScheduledExecutorService scheduler, Gson gson,
|
||||||
|
TransformationServiceProvider transformationServiceProvider) {
|
||||||
ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
|
ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
|
||||||
channelConfigurationJSON, gson, updateListener, tracker)
|
channelConfigurationJSON, gson, updateListener, tracker, scheduler)
|
||||||
.transformationProvider(transformationServiceProvider);
|
.transformationProvider(transformationServiceProvider);
|
||||||
try {
|
try {
|
||||||
switch (haID.component) {
|
switch (haID.component) {
|
||||||
@ -86,16 +89,19 @@ public class CFactory {
|
|||||||
private final ChannelStateUpdateListener updateListener;
|
private final ChannelStateUpdateListener updateListener;
|
||||||
private final AvailabilityTracker tracker;
|
private final AvailabilityTracker tracker;
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
private @Nullable TransformationServiceProvider transformationServiceProvider;
|
private @Nullable TransformationServiceProvider transformationServiceProvider;
|
||||||
|
|
||||||
protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson,
|
protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson,
|
||||||
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker) {
|
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
|
||||||
|
ScheduledExecutorService scheduler) {
|
||||||
this.thingUID = thingUID;
|
this.thingUID = thingUID;
|
||||||
this.haID = haID;
|
this.haID = haID;
|
||||||
this.configJSON = configJSON;
|
this.configJSON = configJSON;
|
||||||
this.gson = gson;
|
this.gson = gson;
|
||||||
this.updateListener = updateListener;
|
this.updateListener = updateListener;
|
||||||
this.tracker = tracker;
|
this.tracker = tracker;
|
||||||
|
this.scheduler = scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComponentConfiguration transformationProvider(
|
public ComponentConfiguration transformationProvider(
|
||||||
@ -133,6 +139,10 @@ public class CFactory {
|
|||||||
return tracker;
|
return tracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScheduledExecutorService getScheduler() {
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
public <C extends BaseChannelConfiguration> C getConfig(Class<C> clazz) {
|
public <C extends BaseChannelConfiguration> C getConfig(Class<C> clazz) {
|
||||||
return BaseChannelConfiguration.fromString(configJSON, gson, clazz);
|
return BaseChannelConfiguration.fromString(configJSON, gson, clazz);
|
||||||
}
|
}
|
||||||
|
@ -69,19 +69,16 @@ public class ComponentAlarmControlPanel extends AbstractComponent<ComponentAlarm
|
|||||||
String command_topic = channelConfiguration.command_topic;
|
String command_topic = channelConfiguration.command_topic;
|
||||||
if (command_topic != null) {
|
if (command_topic != null) {
|
||||||
buildChannel(switchDisarmChannelID, new TextValue(new String[] { channelConfiguration.payload_disarm }),
|
buildChannel(switchDisarmChannelID, new TextValue(new String[] { channelConfiguration.payload_disarm }),
|
||||||
channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.commandTopic(command_topic, channelConfiguration.retain)//
|
.commandTopic(command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
|
|
||||||
buildChannel(switchArmHomeChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_home }),
|
buildChannel(switchArmHomeChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_home }),
|
||||||
channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.commandTopic(command_topic, channelConfiguration.retain)//
|
.commandTopic(command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
|
|
||||||
buildChannel(switchArmAwayChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_away }),
|
buildChannel(switchArmAwayChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_away }),
|
||||||
channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.commandTopic(command_topic, channelConfiguration.retain)//
|
.commandTopic(command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.mqtt.homeassistant.internal;
|
package org.openhab.binding.mqtt.homeassistant.internal;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
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.ChannelStateUpdateListener;
|
||||||
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
||||||
|
import org.openhab.binding.mqtt.generic.values.Value;
|
||||||
|
import org.openhab.binding.mqtt.homeassistant.internal.listener.ExpireUpdateStateListener;
|
||||||
|
import org.openhab.binding.mqtt.homeassistant.internal.listener.OffDelayUpdateStateListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A MQTT BinarySensor, following the https://www.home-assistant.io/components/binary_sensor.mqtt/ specification.
|
* A MQTT BinarySensor, following the https://www.home-assistant.io/components/binary_sensor.mqtt/ specification.
|
||||||
@ -35,23 +41,40 @@ public class ComponentBinarySensor extends AbstractComponent<ComponentBinarySens
|
|||||||
|
|
||||||
protected @Nullable String device_class;
|
protected @Nullable String device_class;
|
||||||
protected boolean force_update = false;
|
protected boolean force_update = false;
|
||||||
protected int expire_after = 0;
|
protected @Nullable Integer expire_after;
|
||||||
|
protected @Nullable Integer off_delay;
|
||||||
|
|
||||||
protected String state_topic = "";
|
protected String state_topic = "";
|
||||||
protected String payload_on = "ON";
|
protected String payload_on = "ON";
|
||||||
protected String payload_off = "OFF";
|
protected String payload_off = "OFF";
|
||||||
|
|
||||||
|
protected @Nullable String json_attributes_topic;
|
||||||
|
protected @Nullable String json_attributes_template;
|
||||||
|
protected @Nullable List<String> json_attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComponentBinarySensor(CFactory.ComponentConfiguration componentConfiguration) {
|
public ComponentBinarySensor(CFactory.ComponentConfiguration componentConfiguration) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class);
|
super(componentConfiguration, ChannelConfiguration.class);
|
||||||
|
|
||||||
if (channelConfiguration.force_update) {
|
OnOffValue value = new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off);
|
||||||
throw new UnsupportedOperationException("Component:Sensor does not support forced updates");
|
|
||||||
|
buildChannel(sensorChannelID, value, "value", getListener(componentConfiguration, value))
|
||||||
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChannelStateUpdateListener getListener(CFactory.ComponentConfiguration componentConfiguration,
|
||||||
|
Value value) {
|
||||||
|
ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
|
||||||
|
|
||||||
|
if (channelConfiguration.expire_after != null) {
|
||||||
|
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expire_after, value,
|
||||||
|
componentConfiguration.getTracker(), componentConfiguration.getScheduler());
|
||||||
|
}
|
||||||
|
if (channelConfiguration.off_delay != null) {
|
||||||
|
updateListener = new OffDelayUpdateStateListener(updateListener, channelConfiguration.off_delay, value,
|
||||||
|
componentConfiguration.getScheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
buildChannel(sensorChannelID, new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off),
|
return updateListener;
|
||||||
channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,7 @@ public class ComponentCamera extends AbstractComponent<ComponentCamera.ChannelCo
|
|||||||
|
|
||||||
ImageValue value = new ImageValue();
|
ImageValue value = new ImageValue();
|
||||||
|
|
||||||
buildChannel(cameraChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
buildChannel(cameraChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.topic)//
|
.stateTopic(channelConfiguration.topic).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,8 @@ public class ComponentCover extends AbstractComponent<ComponentCover.ChannelConf
|
|||||||
RollershutterValue value = new RollershutterValue(channelConfiguration.payload_open,
|
RollershutterValue value = new RollershutterValue(channelConfiguration.payload_open,
|
||||||
channelConfiguration.payload_close, channelConfiguration.payload_stop);
|
channelConfiguration.payload_close, channelConfiguration.payload_stop);
|
||||||
|
|
||||||
buildChannel(switchChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
buildChannel(switchChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)
|
||||||
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,8 @@ public class ComponentFan extends AbstractComponent<ComponentFan.ChannelConfigur
|
|||||||
super(componentConfiguration, ChannelConfiguration.class);
|
super(componentConfiguration, ChannelConfiguration.class);
|
||||||
|
|
||||||
OnOffValue value = new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off);
|
OnOffValue value = new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off);
|
||||||
buildChannel(switchChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
buildChannel(switchChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)
|
||||||
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,22 +105,18 @@ public class ComponentLight extends AbstractComponent<ComponentLight.ChannelConf
|
|||||||
channelConfiguration.payload_off, 100);
|
channelConfiguration.payload_off, 100);
|
||||||
|
|
||||||
// Create three MQTT subscriptions and use this class object as update listener
|
// Create three MQTT subscriptions and use this class object as update listener
|
||||||
switchChannel = buildChannel(switchChannelID, value, channelConfiguration.name, this)//
|
switchChannel = buildChannel(switchChannelID, value, channelConfiguration.name, this)
|
||||||
// Some lights use the value_template field for the template, most use state_value_template
|
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.state_value_template,
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.state_value_template,
|
||||||
channelConfiguration.value_template)//
|
channelConfiguration.value_template)
|
||||||
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain).build(false);
|
||||||
.build(false);
|
|
||||||
|
|
||||||
colorChannel = buildChannel(colorChannelID, value, channelConfiguration.name, this)//
|
colorChannel = buildChannel(colorChannelID, value, channelConfiguration.name, this)
|
||||||
.stateTopic(channelConfiguration.rgb_state_topic, channelConfiguration.rgb_value_template)//
|
.stateTopic(channelConfiguration.rgb_state_topic, channelConfiguration.rgb_value_template)
|
||||||
.commandTopic(channelConfiguration.rgb_command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.rgb_command_topic, channelConfiguration.retain).build(false);
|
||||||
.build(false);
|
|
||||||
|
|
||||||
brightnessChannel = buildChannel(brightnessChannelID, value, channelConfiguration.name, this)//
|
brightnessChannel = buildChannel(brightnessChannelID, value, channelConfiguration.name, this)
|
||||||
.stateTopic(channelConfiguration.brightness_state_topic, channelConfiguration.brightness_value_template)//
|
.stateTopic(channelConfiguration.brightness_state_topic, channelConfiguration.brightness_value_template)
|
||||||
.commandTopic(channelConfiguration.brightness_command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.brightness_command_topic, channelConfiguration.retain).build(false);
|
||||||
.build(false);
|
|
||||||
|
|
||||||
channels.put(colorChannelID, colorChannel);
|
channels.put(colorChannelID, colorChannel);
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,8 @@ public class ComponentLock extends AbstractComponent<ComponentLock.ChannelConfig
|
|||||||
|
|
||||||
buildChannel(switchChannelID,
|
buildChannel(switchChannelID,
|
||||||
new OnOffValue(channelConfiguration.payload_lock, channelConfiguration.payload_unlock),
|
new OnOffValue(channelConfiguration.payload_lock, channelConfiguration.payload_unlock),
|
||||||
channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
channelConfiguration.name, componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)
|
||||||
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain)//
|
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.mqtt.homeassistant.internal;
|
package org.openhab.binding.mqtt.homeassistant.internal;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
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.ChannelStateUpdateListener;
|
||||||
import org.openhab.binding.mqtt.generic.values.NumberValue;
|
import org.openhab.binding.mqtt.generic.values.NumberValue;
|
||||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
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.homeassistant.internal.listener.ExpireUpdateStateListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A MQTT sensor, following the https://www.home-assistant.io/components/sensor.mqtt/ specification.
|
* A MQTT sensor, following the https://www.home-assistant.io/components/sensor.mqtt/ specification.
|
||||||
@ -42,18 +45,18 @@ public class ComponentSensor extends AbstractComponent<ComponentSensor.ChannelCo
|
|||||||
protected @Nullable String unit_of_measurement;
|
protected @Nullable String unit_of_measurement;
|
||||||
protected @Nullable String device_class;
|
protected @Nullable String device_class;
|
||||||
protected boolean force_update = false;
|
protected boolean force_update = false;
|
||||||
protected int expire_after = 0;
|
protected @Nullable Integer expire_after;
|
||||||
|
|
||||||
protected String state_topic = "";
|
protected String state_topic = "";
|
||||||
|
|
||||||
|
protected @Nullable String json_attributes_topic;
|
||||||
|
protected @Nullable String json_attributes_template;
|
||||||
|
protected @Nullable List<String> json_attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComponentSensor(CFactory.ComponentConfiguration componentConfiguration) {
|
public ComponentSensor(CFactory.ComponentConfiguration componentConfiguration) {
|
||||||
super(componentConfiguration, ChannelConfiguration.class);
|
super(componentConfiguration, ChannelConfiguration.class);
|
||||||
|
|
||||||
if (channelConfiguration.force_update) {
|
|
||||||
throw new UnsupportedOperationException("Component:Sensor does not support forced updates");
|
|
||||||
}
|
|
||||||
|
|
||||||
Value value;
|
Value value;
|
||||||
|
|
||||||
String uom = channelConfiguration.unit_of_measurement;
|
String uom = channelConfiguration.unit_of_measurement;
|
||||||
@ -68,8 +71,19 @@ public class ComponentSensor extends AbstractComponent<ComponentSensor.ChannelCo
|
|||||||
|
|
||||||
boolean trigger = triggerIcons.matcher(icon).matches();
|
boolean trigger = triggerIcons.matcher(icon).matches();
|
||||||
|
|
||||||
buildChannel(sensorChannelID, value, channelConfiguration.name, componentConfiguration.getUpdateListener())//
|
buildChannel(sensorChannelID, value, channelConfiguration.name, getListener(componentConfiguration, value))
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
||||||
.trigger(trigger).build();
|
.trigger(trigger).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChannelStateUpdateListener getListener(CFactory.ComponentConfiguration componentConfiguration,
|
||||||
|
Value value) {
|
||||||
|
ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
|
||||||
|
|
||||||
|
if (channelConfiguration.expire_after != null) {
|
||||||
|
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expire_after, value,
|
||||||
|
componentConfiguration.getTracker(), componentConfiguration.getScheduler());
|
||||||
|
}
|
||||||
|
return updateListener;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,9 @@ public class ComponentSwitch extends AbstractComponent<ComponentSwitch.ChannelCo
|
|||||||
OnOffValue value = new OnOffValue(state_on, state_off, channelConfiguration.payload_on,
|
OnOffValue value = new OnOffValue(state_on, state_off, channelConfiguration.payload_on,
|
||||||
channelConfiguration.payload_off);
|
channelConfiguration.payload_off);
|
||||||
|
|
||||||
buildChannel(switchChannelID, value, "state", componentConfiguration.getUpdateListener())//
|
buildChannel(switchChannelID, value, "state", componentConfiguration.getUpdateListener())
|
||||||
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)//
|
.stateTopic(channelConfiguration.state_topic, channelConfiguration.value_template)
|
||||||
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain, channelConfiguration.qos)//
|
.commandTopic(channelConfiguration.command_topic, channelConfiguration.retain, channelConfiguration.qos)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
|||||||
AbstractComponent<?> component = null;
|
AbstractComponent<?> component = null;
|
||||||
|
|
||||||
if (config.length() > 0) {
|
if (config.length() > 0) {
|
||||||
component = CFactory.createComponent(thingUID, haID, config, updateListener, tracker, gson,
|
component = CFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler, gson,
|
||||||
transformationServiceProvider);
|
transformationServiceProvider);
|
||||||
}
|
}
|
||||||
if (component != null) {
|
if (component != null) {
|
||||||
|
@ -17,6 +17,7 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -152,8 +153,8 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
if (channelConfigurationJSON == null) {
|
if (channelConfigurationJSON == null) {
|
||||||
logger.warn("Provided channel does not have a 'config' configuration key!");
|
logger.warn("Provided channel does not have a 'config' configuration key!");
|
||||||
} else {
|
} else {
|
||||||
component = CFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this, gson,
|
component = CFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this, scheduler,
|
||||||
transformationServiceProvider);
|
gson, transformationServiceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component != null) {
|
if (component != null) {
|
||||||
@ -296,8 +297,8 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateThingStatus(boolean messageReceived, boolean availabilityTopicsSeen) {
|
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) {
|
||||||
if (!messageReceived || availabilityTopicsSeen) {
|
if (availabilityTopicsSeen.orElse(messageReceived)) {
|
||||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
||||||
} else {
|
} else {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE);
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.mqtt.homeassistant.internal.listener;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy class for {@link ChannelStateUpdateListener} forwarding everything to the real listener.
|
||||||
|
* <p>
|
||||||
|
* This class is used to be able handle special cases like timeouts.
|
||||||
|
*
|
||||||
|
* @author Jochen Klein - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public abstract class ChannelStateUpdateListenerProxy implements ChannelStateUpdateListener {
|
||||||
|
|
||||||
|
private final ChannelStateUpdateListener original;
|
||||||
|
|
||||||
|
public ChannelStateUpdateListenerProxy(ChannelStateUpdateListener original) {
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateChannelState(@NonNull ChannelUID channelUID, @NonNull State value) {
|
||||||
|
original.updateChannelState(channelUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postChannelCommand(@NonNull ChannelUID channelUID, @NonNull Command value) {
|
||||||
|
original.postChannelCommand(channelUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void triggerChannel(@NonNull ChannelUID channelUID, @NonNull String eventPayload) {
|
||||||
|
original.triggerChannel(channelUID, eventPayload);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.mqtt.homeassistant.internal.listener;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||||
|
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||||
|
import org.openhab.binding.mqtt.generic.values.Value;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener to reset the channel value after a timeout.
|
||||||
|
*
|
||||||
|
* @author Jochen Klein - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ExpireUpdateStateListener extends ChannelStateUpdateListenerProxy {
|
||||||
|
|
||||||
|
private final int expireAfter;
|
||||||
|
private final Value value;
|
||||||
|
private final AvailabilityTracker tracker;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
|
private AtomicReference<@Nullable ScheduledFuture<?>> expire = new AtomicReference<>();
|
||||||
|
|
||||||
|
public ExpireUpdateStateListener(ChannelStateUpdateListener original, int expireAfter, Value value,
|
||||||
|
AvailabilityTracker tracker, ScheduledExecutorService scheduler) {
|
||||||
|
super(original);
|
||||||
|
this.expireAfter = expireAfter;
|
||||||
|
this.value = value;
|
||||||
|
this.tracker = tracker;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateChannelState(final ChannelUID channelUID, State state) {
|
||||||
|
super.updateChannelState(channelUID, state);
|
||||||
|
|
||||||
|
ScheduledFuture<?> oldExpire = expire.getAndSet(scheduler.schedule(() -> {
|
||||||
|
value.resetState();
|
||||||
|
tracker.resetMessageReceived();
|
||||||
|
ExpireUpdateStateListener.super.updateChannelState(channelUID, value.getChannelState());
|
||||||
|
}, expireAfter, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
if (oldExpire != null) {
|
||||||
|
oldExpire.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.mqtt.homeassistant.internal.listener;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||||
|
import org.openhab.binding.mqtt.generic.values.Value;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener to set the binary sensor value to 'off' after a timeout.
|
||||||
|
*
|
||||||
|
* @author Jochen Klein - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OffDelayUpdateStateListener extends ChannelStateUpdateListenerProxy {
|
||||||
|
|
||||||
|
private final int offDelay;
|
||||||
|
private final Value value;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
|
private AtomicReference<@Nullable ScheduledFuture<?>> delay = new AtomicReference<>();
|
||||||
|
|
||||||
|
public OffDelayUpdateStateListener(ChannelStateUpdateListener original, int offDelay, Value value,
|
||||||
|
ScheduledExecutorService scheduler) {
|
||||||
|
super(original);
|
||||||
|
this.offDelay = offDelay;
|
||||||
|
this.value = value;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateChannelState(final ChannelUID channelUID, State state) {
|
||||||
|
super.updateChannelState(channelUID, state);
|
||||||
|
|
||||||
|
ScheduledFuture<?> newDelay = null;
|
||||||
|
|
||||||
|
if (OnOffType.ON == state) {
|
||||||
|
newDelay = scheduler.schedule(() -> {
|
||||||
|
value.update(OnOffType.OFF);
|
||||||
|
OffDelayUpdateStateListener.super.updateChannelState(channelUID, value.getChannelState());
|
||||||
|
}, offDelay, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledFuture<?> oldDelay = delay.getAndSet(newDelay);
|
||||||
|
if (oldDelay != null) {
|
||||||
|
oldDelay.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -246,7 +247,7 @@ public class HomieThingHandler extends AbstractMQTTThingHandler implements Devic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateThingStatus(boolean messageReceived, boolean availabilityTopicsSeen) {
|
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) {
|
||||||
// not used here
|
// not used here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user