Skip to content

Commit

Permalink
[nikobus] added option to set rollershutter position (openhab#11548)
Browse files Browse the repository at this point in the history
Signed-off-by: Boris Krivonog [email protected]
Signed-off-by: Boris Krivonog <[email protected]>
  • Loading branch information
crnjan authored and andan67 committed Nov 5, 2022
1 parent af116ec commit 7dcfc66
Showing 1 changed file with 90 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
Expand All @@ -22,6 +23,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikobus.internal.utils.Utils;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
Expand All @@ -45,7 +47,6 @@
public class NikobusRollershutterModuleHandler extends NikobusModuleHandler {
private final Logger logger = LoggerFactory.getLogger(NikobusRollershutterModuleHandler.class);
private final List<PositionEstimator> positionEstimators = new CopyOnWriteArrayList<>();

private final Map<String, DirectionConfiguration> directionConfigurations = new ConcurrentHashMap<>();

public NikobusRollershutterModuleHandler(Thing thing) {
Expand Down Expand Up @@ -77,59 +78,82 @@ public void initialize() {
logger.debug("Position estimators for {} = {}", thing.getUID(), positionEstimators);
}

@Override
public void dispose() {
positionEstimators.forEach(PositionEstimator::destroy);
super.dispose();
}

@Override
protected int valueFromCommand(String channelId, Command command) {
if (command == StopMoveType.STOP) {
return 0x00;
}
if (command == UpDownType.DOWN || command == StopMoveType.MOVE) {
return getDirectionConfiguration(channelId).down;
}
if (command == UpDownType.UP) {
return getDirectionConfiguration(channelId).up;
Optional<PositionEstimator> positionEstimator = getPositionEstimator(channelId);
if (command instanceof DecimalType) {
return positionEstimator.map(estimator -> {
return estimator.processSetPosition(((DecimalType) command).intValue());
}).orElseThrow(() -> {
throw new IllegalArgumentException(
"Received position request but no estimation configured for channel " + channelId);
});
}
throw new IllegalArgumentException("Command '" + command + "' not supported");
int result = convertCommandToValue(channelId, command);
positionEstimator.ifPresent(PositionEstimator::cancelStopMovement);
return result;
}

@Override
protected State stateFromValue(String channelId, int value) {
if (value == 0x00) {
return OnOffType.OFF;
}

DirectionConfiguration configuration = getDirectionConfiguration(channelId);
if (value == configuration.up) {
return UpDownType.UP;
}
if (value == configuration.down) {
return UpDownType.DOWN;
}

throw new IllegalArgumentException("Unexpected value " + value + " received");
}

@Override
protected void updateState(ChannelUID channelUID, State state) {
logger.debug("updateState {} {}", channelUID, state);

positionEstimators.stream().filter(estimator -> channelUID.equals(estimator.getChannelUID())).findFirst()
.ifPresentOrElse(estimator -> {
if (state == UpDownType.UP) {
estimator.start(-1);
} else if (state == UpDownType.DOWN) {
estimator.start(1);
} else if (state == OnOffType.OFF) {
estimator.stop();
} else {
logger.debug("Unexpected state update '{}' for '{}'", state, channelUID);
}
}, () -> super.updateState(channelUID, state));
getPositionEstimator(channelUID.getId()).ifPresentOrElse(estimator -> {
if (state == UpDownType.UP) {
estimator.start(-1);
} else if (state == UpDownType.DOWN) {
estimator.start(1);
} else if (state == OnOffType.OFF) {
estimator.stop();
} else {
logger.debug("Unexpected state update '{}' for '{}'", state, channelUID);
}
}, () -> super.updateState(channelUID, state));
}

private void updateState(ChannelUID channelUID, int percent) {
super.updateState(channelUID, new PercentType(percent));
}

protected int convertCommandToValue(String channelId, Command command) {
if (command == StopMoveType.STOP) {
return 0x00;
}
if (command == UpDownType.DOWN || command == StopMoveType.MOVE) {
return getDirectionConfiguration(channelId).down;
}
if (command == UpDownType.UP) {
return getDirectionConfiguration(channelId).up;
}
throw new IllegalArgumentException("Command '" + command + "' not supported");
}

private Optional<PositionEstimator> getPositionEstimator(String channelId) {
return positionEstimators.stream().filter(estimator -> channelId.equals(estimator.getChannelUID().getId()))
.findFirst();
}

private DirectionConfiguration getDirectionConfiguration(String channelId) {
DirectionConfiguration configuration = directionConfigurations.get(channelId);
if (configuration == null) {
Expand All @@ -154,6 +178,7 @@ private class PositionEstimator {
private long startTimeMillis = 0;
private int direction = 0;
private @Nullable Future<?> updateEstimateFuture;
private @Nullable Future<?> stopMovementFuture;

PositionEstimator(ChannelUID channelUID, PositionEstimatorConfig config) {
this.channelUID = channelUID;
Expand All @@ -167,6 +192,12 @@ public ChannelUID getChannelUID() {
return channelUID;
}

public void destroy() {
Utils.cancel(updateEstimateFuture);
updateEstimateFuture = null;
cancelStopMovement();
}

public void start(int direction) {
stop();
synchronized (this) {
Expand All @@ -191,6 +222,41 @@ public void stop() {
}
}

public int processSetPosition(int percent) {
if (percent < 0 || percent > 100) {
throw new IllegalArgumentException("Position % out of range - expecting [0, 100] but got " + percent
+ " for " + channelUID.getId());
}

cancelStopMovement();

int newPosition = (int) ((double) percent * (double) durationInMillis / 100.0 + 0.5);
int delta = position - newPosition;

logger.debug("Position set command {} for {}: delta = {}, current pos: {}, new position: {}", percent,
channelUID, delta, position, newPosition);

if (delta == 0) {
return convertCommandToValue(channelUID.getId(), StopMoveType.STOP);
}

int time = Math.abs(delta);
if (percent == 0 || percent == 100) {
time += 5000; // Make sure we get to completely open/closed position.
}

stopMovementFuture = scheduler.schedule(() -> {
handleCommand(channelUID, StopMoveType.STOP);
}, time, TimeUnit.MILLISECONDS);

return convertCommandToValue(channelUID.getId(), delta > 0 ? UpDownType.UP : UpDownType.DOWN);
}

public void cancelStopMovement() {
Utils.cancel(stopMovementFuture);
stopMovementFuture = null;
}

private void updateEstimate() {
int direction;
int ellapsedMillis;
Expand Down

0 comments on commit 7dcfc66

Please sign in to comment.