Skip to content

Commit

Permalink
Merge branch 'openwebnet-shutterRun' into openwbebnet-beta-oh4
Browse files Browse the repository at this point in the history
  • Loading branch information
mvalla committed Feb 9, 2023
2 parents cdcc443 + d5fc81d commit e545a60
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 58 deletions.
26 changes: 13 additions & 13 deletions bundles/org.openhab.binding.openwebnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,31 +257,31 @@ OPEN command to execute: *5*8#134##

#### `shutter` position

For Percent commands and position feedback to work correctly, the `shutterRun` Thing config parameter must be configured equal to the time (in ms) to go from full UP to full DOWN. It's possible to enter a value manually or set `shutterRun=AUTO` (default) to calibrate `shutterRun` automatically.
For Percent commands and position feedback to work correctly, the `shutterRun` Thing config parameter must be configured equal to the time (in milliseconds) to go from full UP to full DOWN. It's possible to enter a value manually or set `shutterRun=AUTO` (default) to calibrate `shutterRun` automatically.

_Automatic calibration of `shutterRun`_
##### Automatic calibration of `shutterRun`

If `shutterRun` is set to AUTO a _UP >> DOWN >> Position%_ cycle will be performed automatically the first time a Percent command is sent to the shutter. The binding then relies on the _STOP_ command sent by the actuator at the stop time set in the actuator's advanced configuration in the My Home Suite. Therefore the binding's automatic calibration of `shutterRun` only works correctly if the stop time is set to the time the shutters need to go from full UP to full DOWN.
When `shutterRun` is set to `AUTO`, a _UP >> DOWN >> Position%_ cycle will be performed automatically the first time a Percent command is sent to the shutter: this way the binding will calculate the shutter "run time" and set the `shutterRun` parameter accordingly.
Note that there is a "stop time" parameter that is usually set on the physical actuator (eg. F411U2) either via virtual configuration (MyHOME_Suite: parameter "Stop time" or "M") or physical configuration (jumpers): check the actuator instructions to configure correctly this parameter before doing an automatic calibration of `shutterRun`.
If the "stop time" of the physical actuator is wrongly set or cannot be modified to the actual "run time", auto calibration cannot be used and `shutterRun` has to be set manually.

Older actuators that come without an ID and do not support advanced configuration via the My Home Suite have a fixed stop time of 60 seconds. In this case auto calibration of the binding cannot be used and `shutterRun` has to be set manually.

_Notes on `shutter` position_
##### Position estimation of the shutter

- if `shutterRun` is not set, or is set to `AUTO` but calibration has not been performed yet, then position estimation will remain `UNDEF` (undefined)
- if `shutterRun` is wrongly set higher than the actual runtime, then position estimation will remain `UNDEF`: try to reduce shutterRun until you find the right value
- if `shutterRun` is wrongly set *higher* than the actual runtime, then position estimation will remain `UNDEF`: try to reduce `shutterRun` until you find the right value
- before adding/configuring roller shutter Things it is suggested to have all roller shutters `UP`, otherwise the Percent command won’t work until the roller shutter is fully rolled up
- if OH is restarted the binding does not know if a shutter position has changed in the meantime, so its position will be `UNDEF`. Move the shutter all `UP`/`DOWN` to synchronize again its position with the binding
- the shutter position is estimated based on UP/DOWN timing: an error of ±2% is normal
- if OH is restarted the binding does not know if a shutter position has changed in the meantime, so its position will be `UNDEF`. Move the shutter all `UP`/`DOWN` to synchronise again its position with the binding
- the shutter position is estimated based on UP/DOWN timings: an error of ±2% is normal

#### Scenario channels

Basic Scenarios and CEN/CEN+ Scenarios channels are [TRIGGER channels](https://www.openhab.org/docs/configuration/rules-dsl.html#channel-based-triggers]): they handle events and do not have a state.

A powerful feature is to detect scenario activations and CEN/CEN+ buttons press events to trigger rules in openHAB: this way openHAB becomes a very powerful scenario manager activated by BTicino scenario control modules or by CEN/CEN+ scenarios physical buttons.
See [openwebnet.rules](#openwebnetrules) for examples on how to define rules that trigger on scenarios and on CEN/CEN+ button press events.
See [openwebnet.rules](#openwebnet-rules) for examples on how to define rules that trigger on scenarios and on CEN/CEN+ button press events.

It's also possible to send _virtual scenario activation_ and _virtual press_ events on the BUS, for example to enable the activation of MH202 or F420 scenarios from openHAB..
See [openwebnet.sitemap](#openwebnetsitemap) & [openwebnet.rules](#openwebnetrules) sections for examples on how to use the `activateScenario` and `virtualPress` actions connected to a pushbutton on a sitemap.
See [openwebnet.sitemap](#openwebnet-sitemap) & [openwebnet.rules](#openwebnet-rules) sections for examples on how to use the `activateScenario` and `virtualPress` actions connected to a pushbutton on a sitemap.

- basic scenario channels are named `scenario` and possible events are: `SCENARIO_01` ... `SCENARIO_16` (or up to `SCENARIO_20` in case of module IR3456) when a scenario is activated
- CEN/CEN+ channels are named `button#X` where `X` is the button number on the CEN/CEN+ Scenario Control device
Expand Down Expand Up @@ -319,7 +319,7 @@ BUS gateway and things configuration:
Bridge openwebnet:bus_gateway:mybridge "MyHOMEServer1" [ host="192.168.1.35", passwd="abcde", port=20000, discoveryByActivation=false ] {
bus_on_off_switch LR_switch "Living Room Light" [ where="51" ]
bus_dimmer LR_dimmer "Living Room Dimmer" [ where="0311#4#01" ]
bus_automation LR_shutter "Living Room Shutter" [ where="93", shutterRun="10050"]
bus_automation LR_shutter "Living Room Shutter" [ where="93", shutterRun="20050"]

bus_energy_meter CENTRAL_Ta "Energy Meter Ta" [ where="51" ]
bus_energy_meter CENTRAL_Tb "Energy Meter Tb" [ where="52" ]
Expand Down Expand Up @@ -438,7 +438,7 @@ sitemap openwebnet label="OpenWebNet Binding Example Sitemap"
Frame label="Living Room Thermo"
{
Default item=iLR_zone_temp label="Temperature" icon="fire" valuecolor=[<20="red"]
Setpoint item=iLR_zone_set label="Setpoint [%.1f °C]" step=0.5 minValue=15 maxValue=30
Setpoint item=iLR_zone_setTemp label="Setpoint [%.1f °C]" step=0.5 minValue=15 maxValue=30
Selection item=iLR_zone_fanSpeed label="Fan Speed" icon="fan" mappings=[AUTO="AUTO", SPEED_1="Low", SPEED_2="Medium", SPEED_3="High"]
Switch item=iLR_zone_mode label="Mode" icon="settings"
Selection item=iLR_zone_func label="Function" icon="heating" mappings=[HEATING="Heating", COOLING="Cooling", GENERIC="Heating/Cooling"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
import org.slf4j.LoggerFactory;

/**
* The {@link OpenWebNetAutomationHandler} is responsible for handling commands/messages for an Automation OpenWebNet
* The {@link OpenWebNetAutomationHandler} is responsible for handling
* commands/messages for an Automation OpenWebNet
* device. It extends the abstract {@link OpenWebNetThingHandler}.
*
* @author Massimo Valla - Initial contribution
Expand Down Expand Up @@ -247,18 +248,21 @@ private void handlePercentCommand(PercentType command, String w) {
positionRequested = percent;
} else if (shutterRun >= 1000 && positionEstimation != POSITION_UNKNOWN) {
// these two must be known to calculate moveTime.
// Calculate how much time we have to move and set a deadline to stop after that time
// Calculate how much time we have to move and set a deadline to stop after that
// time
int moveTime = Math
.round(((float) Math.abs(percent - positionEstimation) / POSITION_MAX_STEPS * shutterRun));
logger.debug("# {} # target moveTime={}", deviceWhere, moveTime);
if (moveTime > MIN_STEP_TIME_MSEC) {
ScheduledFuture<?> mSch = moveSchedule;
if (mSch != null && !mSch.isDone()) {
// a moveSchedule was already scheduled and is not done... let's cancel the schedule
// a moveSchedule was already scheduled and is not done... let's cancel the
// schedule
mSch.cancel(false);
logger.debug("# {} # new XX% requested, old moveSchedule cancelled", deviceWhere);
}
// send a requestFirmwareVersion message to BUS gateways to wake up the CMD connection before
// send a requestFirmwareVersion message to BUS gateways to wake up the CMD
// connection before
// sending further cmds
OpenWebNetBridgeHandler h = bridgeHandler;
if (h != null && h.isBusGateway()) {
Expand All @@ -270,7 +274,8 @@ private void handlePercentCommand(PercentType command, String w) {
}
}
}
// REMINDER: start the schedule BEFORE sending the command, because the synch command waits for
// REMINDER: start the schedule BEFORE sending the command, because the synch
// command waits for
// ACK and can take some 300ms
logger.debug("# {} # Starting schedule...", deviceWhere);
moveSchedule = scheduler.schedule(() -> {
Expand All @@ -294,7 +299,7 @@ private void handlePercentCommand(PercentType command, String w) {
}
} else {
logger.info(
"Command {} cannot be executed: unknown position or shutterRun configuration params not/wrongly set (thing={})",
"Command {} cannot be executed: UNDEF position or shutterRun configuration parameter not/wrongly set (thing={})",
command, thing.getUID());
}
}
Expand All @@ -312,12 +317,14 @@ protected String ownIdPrefix() {
protected void handleMessage(BaseOpenMessage msg) {
logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
updateAutomationState((Automation) msg);
// REMINDER: update automation state, and only after update thing status using the super method, to avoid delays
// REMINDER: update automation state, and only after update thing status using
// the super method, to avoid delays
super.handleMessage(msg);
}

/**
* Updates automation device state based on the Automation message received from OWN network
* Updates automation device state based on the Automation message received from
* OWN network
*
* @param msg the Automation message
*/
Expand Down Expand Up @@ -345,10 +352,12 @@ private void updateAutomationState(Automation msg) {
logger.debug("& {} & CALIBRATION - started going ALL DOWN...", deviceWhere);
}
} else if (msg.isStop()) {
long stoppedAt = System.currentTimeMillis();
long measuredRuntime = System.currentTimeMillis() - startedMovingAtTS;
if (calibrating == CALIBRATION_GOING_DOWN && shutterRun == SHUTTER_RUN_UNDEFINED) {
shutterRun = (int) (stoppedAt - startedMovingAtTS);
logger.debug("& {} & CALIBRATION - reached DOWN ---> shutterRun={}", deviceWhere, shutterRun);
// since there are transmission delays we set shutterRun slightly less (-500ms
// and -2%) than measuredRuntime
shutterRun = (int) ((measuredRuntime - 500) * 0.98);
logger.debug("& {} & CALIBRATION - reached DOWN : measuredRuntime={}", deviceWhere, measuredRuntime);
updateMovingState(MOVING_STATE_STOPPED);
logger.debug("& {} & CALIBRATION - COMPLETED, now going to {}%", deviceWhere, positionRequested);
handleShutterCommand(new PercentType(positionRequested));
Expand Down Expand Up @@ -416,7 +425,8 @@ private void updateMovingState(int newState) {
}

/**
* Updates positionEstimation and then channel state based on movedTime and current movingState
* Updates positionEstimation and then channel state based on movedTime and
* current movingState
*/
private void updatePosition() {
int newPos = POSITION_UNKNOWN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ public void onEventMessage(@Nullable OpenMessage msg) {
|| (discoveryByActivation && !scanIsActive))) {
discoverByActivation(baseMsg);
} else {
logger.debug("ownId={} has NO DEVICE associated, ignoring it", ownId);
logger.debug("ownId={} has NO DEVICE associated to bridge {}: ignoring it", ownId, thing.getUID());
}
} else {
deviceHandler.handleMessage(baseMsg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
import org.slf4j.LoggerFactory;

/**
* The {@link OpenWebNetThingHandler} is responsible for handling commands for an OpenWebNet device.
* It's the abstract class for all OpenWebNet things. It should be extended by each specific OpenWebNet category of
* device (WHO).
* The {@link OpenWebNetThingHandler} is responsible for handling commands for
* an OpenWebNet device.
* It's the abstract class for all OpenWebNet things. It should be extended by
* each specific OpenWebNet category of device (WHO).
*
* @author Massimo Valla - Initial contribution
*/
Expand Down Expand Up @@ -118,7 +119,7 @@ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/unknown.waiting-state");
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "@text/offline.bridge-offline");
}
}

Expand Down Expand Up @@ -157,8 +158,8 @@ public void handleCommand(ChannelUID channel, Command command) {
protected abstract void handleChannelCommand(ChannelUID channel, Command command);

/**
* Handle incoming message from OWN network via bridge Thing, directed to this device.
* It should be further implemented by each specific device handler.
* Handle incoming message from OWN network via bridge Thing, directed to this
* device. It should be further implemented by each specific device handler.
*
* @param msg the message to handle
*/
Expand Down Expand Up @@ -203,8 +204,8 @@ protected void handleMessage(BaseOpenMessage msg) {
}

/**
* Request the state for the specified channel. If no answer is received within THING_STATE_REQ_TIMEOUT_SEC, it is
* put OFFLINE.
* Request the state for the specified channel. If no answer is received within
* THING_STATE_REQ_TIMEOUT_SEC, it is put OFFLINE.
* The method must be further implemented by each specific handler.
*
* @param channel the {@link ChannelUID} to request the state for
Expand All @@ -217,7 +218,8 @@ protected void requestChannelState(ChannelUID channel) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-where");
return;
}
// set a schedule to put device OFFLINE if no answer is received after THING_STATE_REQ_TIMEOUT_SEC
// set a schedule to put device OFFLINE if no answer is received after
// THING_STATE_REQ_TIMEOUT_SEC
requestChannelStateTimeout = scheduler.schedule(() -> {
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.debug("requestChannelState() TIMEOUT for thing {}", thing.getUID());
Expand All @@ -228,21 +230,24 @@ protected void requestChannelState(ChannelUID channel) {
}

/**
* Refresh a device, possibly using a single OWN command if refreshAll=true and if supported.
* The method must be further implemented by each specific handler.
* Refresh a device, possibly using a single OWN command if refreshAll=true and
* if supported. The method must be further implemented by each specific
* handler.
*
* @param refreshAll true if all devices for this handler must be refreshed with a single OWN command, if supported,
* otherwise just refresh the single device.
* @param refreshAll true if all devices for this handler must be refreshed with
* a single OWN command, if supported, otherwise just refresh
* the single device.
*/
protected abstract void refreshDevice(boolean refreshAll);

/**
* If the subclass supports refreshing all devices with a single OWN command, returns the last TS when a refreshAll
* was requested, or 0 if not requested yet. If not supported return -1 (default).
* If the subclass supports refreshing all devices with a single OWN command,
* returns the last TS when a refreshAll was requested, or 0 if not requested
* yet. If not supported return -1 (default).
* It must be implemented by each subclass that supports all devices refresh.
*
* @return timestamp when last refreshAll command was sent, 0 if not requested yet, or -1 if it's not supported by
* subclass.
* @return timestamp when last refreshAll command was sent, 0 if not requested
* yet, or -1 if it's not supported by subclass.
*/
protected long getRefreshAllLastTS() {
return -1;
Expand All @@ -266,8 +271,8 @@ protected void refreshAllDevices() {
logger.debug("--- refreshAllDevices() : refresh all devices sent {}msec ago, skipping... ({})",
ALL_DEVICES_REFRESH_INTERVAL_MSEC, thing.getUID());
}
// sometimes GENERAL (e.g. #*1*0##) refresh requests do not return state for all devices, so let's
// schedule another single refresh device, just in case
// sometimes GENERAL (e.g. #*1*0##) refresh requests do not return state for all
// devices, so let's schedule another single refresh device, just in case
refreshTimeout = scheduler.schedule(() -> {
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.debug(
Expand All @@ -286,8 +291,9 @@ protected void refreshAllDevices() {
}

/**
* Abstract builder for device Where address, to be implemented by each subclass to choose the right Where subclass
* (the method is used only if the Thing is associated to a BUS gateway).
* Abstract builder for device Where address, to be implemented by each subclass
* to choose the right Where subclass (the method is used only if the Thing is
* associated to a BUS gateway).
*
* @param wStr the WHERE string
*/
Expand All @@ -312,7 +318,8 @@ public void dispose() {
}

/**
* Helper method to return a Quantity from a Number value or UnDefType.NULL if value is null
* Helper method to return a Quantity from a Number value or UnDefType.NULL if
* value is null
*
* @param value to be used
* @param unit to be used
Expand All @@ -323,7 +330,8 @@ protected <U extends Quantity<U>> State getAsQuantityTypeOrNull(@Nullable Number
}

/**
* Returns a prefix String for ownId specific for each handler. To be implemented by sub-classes.
* Returns a prefix String for ownId specific for each handler.
* To be implemented by sub-classes.
*
* @return
*/
Expand Down
Loading

0 comments on commit e545a60

Please sign in to comment.