added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.openwebnet-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-openwebnet" description="OpenWebNet (BTicino/Legrand) Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-serial</feature>
|
||||
<feature>openhab-transport-upnp</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.openwebnet/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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.openwebnet;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetBindingConstants} class defines common constants, which are used across the whole binding.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "openwebnet";
|
||||
|
||||
public static final int THING_STATE_REQ_TIMEOUT_SEC = 5;
|
||||
|
||||
// #LIST OF Thing Type UIDs
|
||||
// generic device (used for not identified devices)
|
||||
public static final ThingTypeUID THING_TYPE_GENERIC_DEVICE = new ThingTypeUID(BINDING_ID, "generic_device");
|
||||
public static final String THING_LABEL_GENERIC_DEVICE = "GENERIC Device";
|
||||
// bridges
|
||||
public static final ThingTypeUID THING_TYPE_ZB_GATEWAY = new ThingTypeUID(BINDING_ID, "zb_gateway");
|
||||
public static final String THING_LABEL_ZB_GATEWAY = "ZigBee USB Gateway";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_GATEWAY = new ThingTypeUID(BINDING_ID, "bus_gateway");
|
||||
public static final String THING_LABEL_BUS_GATEWAY = "BUS Gateway";
|
||||
// other thing types
|
||||
// BUS
|
||||
public static final ThingTypeUID THING_TYPE_BUS_ON_OFF_SWITCH = new ThingTypeUID(BINDING_ID, "bus_on_off_switch");
|
||||
public static final String THING_LABEL_BUS_ON_OFF_SWITCH = "Switch";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_DIMMER = new ThingTypeUID(BINDING_ID, "bus_dimmer");
|
||||
public static final String THING_LABEL_BUS_DIMMER = "Dimmer";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_AUTOMATION = new ThingTypeUID(BINDING_ID, "bus_automation");
|
||||
public static final String THING_LABEL_BUS_AUTOMATION = "Automation";
|
||||
|
||||
// ZIGBEE
|
||||
public static final ThingTypeUID THING_TYPE_ZB_ON_OFF_SWITCH = new ThingTypeUID(BINDING_ID, "zb_on_off_switch");
|
||||
public static final String THING_LABEL_ZB_ON_OFF_SWITCH = "ZigBee Switch";
|
||||
public static final ThingTypeUID THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS = new ThingTypeUID(BINDING_ID,
|
||||
"zb_on_off_switch2u");
|
||||
public static final String THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS = "ZigBee 2-units Switch";
|
||||
public static final ThingTypeUID THING_TYPE_ZB_DIMMER = new ThingTypeUID(BINDING_ID, "zb_dimmer");
|
||||
public static final String THING_LABEL_ZB_DIMMER = "ZigBee Dimmer";
|
||||
public static final ThingTypeUID THING_TYPE_ZB_AUTOMATION = new ThingTypeUID(BINDING_ID, "zb_automation");
|
||||
public static final String THING_LABEL_ZB_AUTOMATION = "ZigBee Automation";
|
||||
|
||||
// #SUPPORTED THINGS SETS
|
||||
// ## Generic
|
||||
public static final Set<ThingTypeUID> GENERIC_SUPPORTED_THING_TYPES = new HashSet<>(
|
||||
Arrays.asList(THING_TYPE_GENERIC_DEVICE));
|
||||
// ## Lighting
|
||||
public static final Set<ThingTypeUID> LIGHTING_SUPPORTED_THING_TYPES = new HashSet<>(
|
||||
Arrays.asList(THING_TYPE_ZB_ON_OFF_SWITCH, THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS, THING_TYPE_ZB_DIMMER,
|
||||
THING_TYPE_BUS_ON_OFF_SWITCH, THING_TYPE_BUS_DIMMER));
|
||||
// ## Automation
|
||||
public static final Set<ThingTypeUID> AUTOMATION_SUPPORTED_THING_TYPES = new HashSet<>(
|
||||
Arrays.asList(THING_TYPE_ZB_AUTOMATION, THING_TYPE_BUS_AUTOMATION));
|
||||
|
||||
// ## Groups
|
||||
public static final Set<ThingTypeUID> DEVICE_SUPPORTED_THING_TYPES = Stream
|
||||
.of(LIGHTING_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES, GENERIC_SUPPORTED_THING_TYPES)
|
||||
.flatMap(Collection::stream).collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
public static final Set<ThingTypeUID> BRIDGE_SUPPORTED_THING_TYPES = new HashSet<>(
|
||||
Arrays.asList(THING_TYPE_ZB_GATEWAY, THING_TYPE_BUS_GATEWAY));
|
||||
|
||||
public static final Set<ThingTypeUID> ALL_SUPPORTED_THING_TYPES = Stream
|
||||
.of(DEVICE_SUPPORTED_THING_TYPES, BRIDGE_SUPPORTED_THING_TYPES).flatMap(Collection::stream)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
// LIST OF ALL CHANNEL IDs
|
||||
// lighting
|
||||
public static final String CHANNEL_SWITCH = "switch";
|
||||
public static final String CHANNEL_SWITCH_01 = "switch_01";
|
||||
public static final String CHANNEL_SWITCH_02 = "switch_02";
|
||||
public static final String CHANNEL_BRIGHTNESS = "brightness";
|
||||
// automation
|
||||
public static final String CHANNEL_SHUTTER = "shutter";
|
||||
|
||||
// devices config properties
|
||||
public static final String CONFIG_PROPERTY_WHERE = "where";
|
||||
public static final String CONFIG_PROPERTY_SHUTTER_RUN = "shutterRun";
|
||||
|
||||
// BUS gw config properties
|
||||
public static final String CONFIG_PROPERTY_HOST = "host";
|
||||
// properties
|
||||
public static final String PROPERTY_OWNID = "ownId";
|
||||
public static final String PROPERTY_ZIGBEEID = "zigbeeid";
|
||||
public static final String PROPERTY_FIRMWARE_VERSION = "firmwareVersion";
|
||||
public static final String PROPERTY_MODEL = "model";
|
||||
public static final String PROPERTY_SERIAL_NO = "serialNumber";
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* 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.openwebnet.handler;
|
||||
|
||||
import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.CHANNEL_SHUTTER;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.openwebnet4j.OpenGateway;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.message.Automation;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.GatewayMgmt;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetAutomationHandler.class);
|
||||
|
||||
private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("ss.SSS");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.AUTOMATION_SUPPORTED_THING_TYPES;
|
||||
|
||||
// moving states
|
||||
public static final int MOVING_STATE_STOPPED = 0;
|
||||
public static final int MOVING_STATE_MOVING_UP = 1;
|
||||
public static final int MOVING_STATE_MOVING_DOWN = 2;
|
||||
public static final int MOVING_STATE_UNKNOWN = -1;
|
||||
|
||||
// calibration states
|
||||
public static final int CALIBRATION_INACTIVE = -1;
|
||||
public static final int CALIBRATION_ACTIVATED = 0;
|
||||
public static final int CALIBRATION_GOING_UP = 1;
|
||||
public static final int CALIBRATION_GOING_DOWN = 2;
|
||||
|
||||
// positions
|
||||
public static final int POSITION_MAX_STEPS = 100;
|
||||
public static final int POSITION_DOWN = 100;
|
||||
public static final int POSITION_UP = 0;
|
||||
public static final int POSITION_UNKNOWN = -1;
|
||||
|
||||
public static final int SHUTTER_RUN_UNDEFINED = -1;
|
||||
private int shutterRun = SHUTTER_RUN_UNDEFINED;
|
||||
private static final String AUTO_CALIBRATION = "AUTO";
|
||||
|
||||
private long startedMovingAt = -1;
|
||||
private int movingState = MOVING_STATE_UNKNOWN;
|
||||
private int positionEstimation = POSITION_UNKNOWN;
|
||||
private @Nullable ScheduledFuture<?> moveSchedule;
|
||||
private int positionRequested = POSITION_UNKNOWN;
|
||||
private int calibrating = CALIBRATION_INACTIVE;
|
||||
private static final int MIN_STEP_TIME_MSEC = 50;
|
||||
private @Nullable Command commandRequestedWhileMoving = null;
|
||||
|
||||
public OpenWebNetAutomationHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
Object shutterRunConfig = getConfig().get(OpenWebNetBindingConstants.CONFIG_PROPERTY_SHUTTER_RUN);
|
||||
try {
|
||||
if (shutterRunConfig == null) {
|
||||
shutterRunConfig = AUTO_CALIBRATION;
|
||||
logger.debug("shutterRun null --> default to AUTO");
|
||||
} else if (shutterRunConfig instanceof String) {
|
||||
if (AUTO_CALIBRATION.equalsIgnoreCase(((String) shutterRunConfig))) {
|
||||
logger.debug("shutterRun set to AUTO via configuration");
|
||||
shutterRun = SHUTTER_RUN_UNDEFINED; // reset shutterRun
|
||||
} else { // try to parse int>=1000
|
||||
int shutterRunInt = Integer.parseInt((String) shutterRunConfig);
|
||||
if (shutterRunInt < 1000) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
shutterRun = shutterRunInt;
|
||||
logger.debug("shutterRun set to {} via configuration", shutterRun);
|
||||
}
|
||||
} else {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Wrong configuration: {} setting must be {} or an integer >= 1000",
|
||||
OpenWebNetBindingConstants.CONFIG_PROPERTY_SHUTTER_RUN, AUTO_CALIBRATION);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"@text/offline.wrong-configuration");
|
||||
shutterRun = SHUTTER_RUN_UNDEFINED;
|
||||
}
|
||||
updateState(CHANNEL_SHUTTER, UnDefType.UNDEF);
|
||||
positionEstimation = POSITION_UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestChannelState(ChannelUID channel) {
|
||||
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
try {
|
||||
send(Automation.requestStatus(w.value()));
|
||||
} catch (OWNException e) {
|
||||
logger.debug("Exception while requesting channel {} state: {}", channel, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleChannelCommand(ChannelUID channel, Command command) {
|
||||
switch (channel.getId()) {
|
||||
case CHANNEL_SHUTTER:
|
||||
handleShutterCommand(command);
|
||||
break;
|
||||
default: {
|
||||
logger.info("Unsupported channel UID {}", channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Automation Roller shutter command (UP/DOWN, STOP/MOVE, PERCENT xx%)
|
||||
*/
|
||||
private void handleShutterCommand(Command command) {
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
calibrating = CALIBRATION_INACTIVE; // cancel calibration if we receive a command
|
||||
commandRequestedWhileMoving = null;
|
||||
try {
|
||||
if (StopMoveType.STOP.equals(command)) {
|
||||
send(Automation.requestStop(w.value()));
|
||||
} else if (command instanceof UpDownType || command instanceof PercentType) {
|
||||
if (movingState == MOVING_STATE_MOVING_UP || movingState == MOVING_STATE_MOVING_DOWN) { // already
|
||||
// moving
|
||||
logger.debug("# {} # already moving, STOP then defer command", deviceWhere);
|
||||
commandRequestedWhileMoving = command;
|
||||
sendHighPriority(Automation.requestStop(w.value()));
|
||||
return;
|
||||
} else {
|
||||
if (command instanceof UpDownType) {
|
||||
if (UpDownType.UP.equals(command)) {
|
||||
send(Automation.requestMoveUp(w.value()));
|
||||
} else {
|
||||
send(Automation.requestMoveDown(w.value()));
|
||||
}
|
||||
} else if (command instanceof PercentType) {
|
||||
handlePercentCommand((PercentType) command, w.value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unsupported command {} for thing {}", command, thing.getUID());
|
||||
}
|
||||
} catch (OWNException e) {
|
||||
logger.debug("Exception while sending request for command {}: {}", command, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Automation PERCENT xx% command
|
||||
*/
|
||||
private void handlePercentCommand(PercentType command, String w) {
|
||||
int percent = command.intValue();
|
||||
if (percent == positionEstimation) {
|
||||
logger.debug("# {} # handlePercentCommand() Command {}% == positionEstimation -> nothing to do", w,
|
||||
percent);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (percent == POSITION_DOWN) { // GO TO 100%
|
||||
send(Automation.requestMoveDown(w));
|
||||
} else if (percent == POSITION_UP) { // GO TO 0%
|
||||
send(Automation.requestMoveUp(w));
|
||||
} else { // GO TO XX%
|
||||
logger.debug("# {} # {}% requested", deviceWhere, percent);
|
||||
if (shutterRun == SHUTTER_RUN_UNDEFINED) {
|
||||
logger.debug("& {} & CALIBRATION - shutterRun not configured, starting CALIBRATION...",
|
||||
deviceWhere);
|
||||
calibrating = CALIBRATION_ACTIVATED;
|
||||
send(Automation.requestMoveUp(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
|
||||
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
|
||||
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
|
||||
// sending further cmds
|
||||
OpenWebNetBridgeHandler h = bridgeHandler;
|
||||
if (h != null && h.isBusGateway()) {
|
||||
OpenGateway gw = h.gateway;
|
||||
if (gw != null) {
|
||||
if (!gw.isCmdConnectionReady()) {
|
||||
logger.debug("# {} # waking-up CMD connection...", deviceWhere);
|
||||
send(GatewayMgmt.requestFirmwareVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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(() -> {
|
||||
logger.debug("# {} # moveSchedule expired, sending STOP...", deviceWhere);
|
||||
try {
|
||||
sendHighPriority(Automation.requestStop(w));
|
||||
} catch (OWNException ex) {
|
||||
logger.debug("Exception while sending request for command {}: {}", command,
|
||||
ex.getMessage(), ex);
|
||||
}
|
||||
}, moveTime, TimeUnit.MILLISECONDS);
|
||||
logger.debug("# {} # ...schedule started, now sending highPriority command...", deviceWhere);
|
||||
if (percent < positionEstimation) {
|
||||
sendHighPriority(Automation.requestMoveUp(w));
|
||||
} else {
|
||||
sendHighPriority(Automation.requestMoveDown(w));
|
||||
}
|
||||
logger.debug("# {} # ...gateway.sendHighPriority() returned", deviceWhere);
|
||||
} else {
|
||||
logger.debug("# {} # moveTime <= MIN_STEP_TIME_MSEC ---> do nothing", deviceWhere);
|
||||
}
|
||||
} else {
|
||||
logger.info(
|
||||
"Command {} cannot be executed: unknown position or shutterRun configuration params not/wrongly set (thing={})",
|
||||
command, thing.getUID());
|
||||
}
|
||||
}
|
||||
} catch (OWNException e) {
|
||||
logger.debug("Exception while sending request for command {}: {}", command, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String ownIdPrefix() {
|
||||
return Who.AUTOMATION.value().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
updateAutomationState((Automation) msg);
|
||||
// REMINDER: update state, then update thing status in the super method, to avoid delays
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates automation device state based on the Automation message received from OWN network
|
||||
*
|
||||
* @param msg the Automation message
|
||||
*/
|
||||
private void updateAutomationState(Automation msg) {
|
||||
logger.debug("updateAutomationState() - msg={} what={}", msg, msg.getWhat());
|
||||
try {
|
||||
if (msg.isCommandTranslation()) {
|
||||
logger.debug("msg is command translation, ignoring it");
|
||||
return;
|
||||
}
|
||||
} catch (FrameException fe) {
|
||||
logger.warn("Exception while checking WHERE command translation for frame {}: {}, ignoring it", msg,
|
||||
fe.getMessage());
|
||||
}
|
||||
if (msg.isUp()) {
|
||||
updateMovingState(MOVING_STATE_MOVING_UP);
|
||||
if (calibrating == CALIBRATION_ACTIVATED) {
|
||||
calibrating = CALIBRATION_GOING_UP;
|
||||
logger.debug("& {} & CALIBRATION - started going ALL UP...", deviceWhere);
|
||||
}
|
||||
} else if (msg.isDown()) {
|
||||
updateMovingState(MOVING_STATE_MOVING_DOWN);
|
||||
if (calibrating == CALIBRATION_ACTIVATED) {
|
||||
calibrating = CALIBRATION_GOING_DOWN;
|
||||
logger.debug("& {} & CALIBRATION - started going ALL DOWN...", deviceWhere);
|
||||
}
|
||||
} else if (msg.isStop()) {
|
||||
long stoppedAt = System.currentTimeMillis();
|
||||
if (calibrating == CALIBRATION_GOING_DOWN && shutterRun == SHUTTER_RUN_UNDEFINED) {
|
||||
shutterRun = (int) (stoppedAt - startedMovingAt);
|
||||
logger.debug("& {} & CALIBRATION - reached DOWN ---> shutterRun={}", deviceWhere, shutterRun);
|
||||
updateMovingState(MOVING_STATE_STOPPED);
|
||||
logger.debug("& {} & CALIBRATION - COMPLETED, now going to {}%", deviceWhere, positionRequested);
|
||||
handleShutterCommand(new PercentType(positionRequested));
|
||||
Configuration configuration = editConfiguration();
|
||||
configuration.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_SHUTTER_RUN, Integer.toString(shutterRun));
|
||||
updateConfiguration(configuration);
|
||||
logger.debug("& {} & CALIBRATION - configuration updated: shutterRun = {}ms", deviceWhere, shutterRun);
|
||||
} else if (calibrating == CALIBRATION_GOING_UP) {
|
||||
updateMovingState(MOVING_STATE_STOPPED);
|
||||
logger.debug("& {} & CALIBRATION - reached UP, now sending DOWN command...", deviceWhere);
|
||||
calibrating = CALIBRATION_ACTIVATED;
|
||||
if (deviceWhere != null) {
|
||||
String w = deviceWhere.value();
|
||||
try {
|
||||
send(Automation.requestMoveDown(w));
|
||||
} catch (OWNException e) {
|
||||
logger.debug("Exception while sending DOWN command during calibration: {}", e.getMessage(), e);
|
||||
calibrating = CALIBRATION_INACTIVE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateMovingState(MOVING_STATE_STOPPED);
|
||||
// do deferred command, if present
|
||||
Command cmd = commandRequestedWhileMoving;
|
||||
if (cmd != null) {
|
||||
handleShutterCommand(cmd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Frame {} not supported for thing {}, ignoring it.", msg, thing.getUID());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates movingState to newState
|
||||
*/
|
||||
private void updateMovingState(int newState) {
|
||||
if (movingState == MOVING_STATE_STOPPED) {
|
||||
if (newState != MOVING_STATE_STOPPED) { // moving after stop
|
||||
startedMovingAt = System.currentTimeMillis();
|
||||
logger.debug("# {} # MOVING {} - startedMovingAt={} - {}", deviceWhere, newState, startedMovingAt,
|
||||
FORMATTER.format(new Date(startedMovingAt)));
|
||||
}
|
||||
} else { // we were moving
|
||||
updatePosition();
|
||||
if (newState != MOVING_STATE_STOPPED) { // moving after moving, take new timestamp
|
||||
startedMovingAt = System.currentTimeMillis();
|
||||
logger.debug("# {} # MOVING {} - startedMovingAt={} - {}", deviceWhere, newState, startedMovingAt,
|
||||
FORMATTER.format(new Date(startedMovingAt)));
|
||||
}
|
||||
// cancel the schedule
|
||||
ScheduledFuture<?> mSc = moveSchedule;
|
||||
if (mSc != null && !mSc.isDone()) {
|
||||
mSc.cancel(false);
|
||||
}
|
||||
}
|
||||
movingState = newState;
|
||||
logger.debug("# {} # movingState={} positionEstimation={} - calibrating={} shutterRun={}", deviceWhere,
|
||||
movingState, positionEstimation, calibrating, shutterRun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates positionEstimation and then channel state based on movedTime and current movingState
|
||||
*/
|
||||
private void updatePosition() {
|
||||
int newPos = POSITION_UNKNOWN;
|
||||
if (shutterRun > 0) {// we have shutterRun defined, let's calculate new positionEstimation
|
||||
long movedTime = System.currentTimeMillis() - startedMovingAt;
|
||||
logger.debug("# {} # current positionEstimation={} movedTime={}", deviceWhere, positionEstimation,
|
||||
movedTime);
|
||||
int movedSteps = Math.round((float) movedTime / shutterRun * POSITION_MAX_STEPS);
|
||||
logger.debug("# {} # movedSteps: {} {}", deviceWhere, movedSteps,
|
||||
(movingState == MOVING_STATE_MOVING_DOWN) ? "DOWN(+)" : "UP(-)");
|
||||
if (positionEstimation == POSITION_UNKNOWN && movedSteps >= POSITION_MAX_STEPS) { // we did a full run
|
||||
newPos = (movingState == MOVING_STATE_MOVING_DOWN) ? POSITION_DOWN : POSITION_UP;
|
||||
} else if (positionEstimation != POSITION_UNKNOWN) {
|
||||
newPos = positionEstimation
|
||||
+ ((movingState == MOVING_STATE_MOVING_DOWN) ? movedSteps : -1 * movedSteps);
|
||||
logger.debug("# {} # {} {} {} = {}", deviceWhere, positionEstimation,
|
||||
(movingState == MOVING_STATE_MOVING_DOWN) ? "+" : "-", movedSteps, newPos);
|
||||
if (newPos > POSITION_DOWN) {
|
||||
newPos = POSITION_DOWN;
|
||||
} else if (newPos < POSITION_UP) {
|
||||
newPos = POSITION_UP;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newPos != POSITION_UNKNOWN) {
|
||||
if (newPos != positionEstimation) {
|
||||
updateState(CHANNEL_SHUTTER, new PercentType(newPos));
|
||||
}
|
||||
} else {
|
||||
updateState(CHANNEL_SHUTTER, UnDefType.UNDEF);
|
||||
}
|
||||
positionEstimation = newPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> mSc = moveSchedule;
|
||||
if (mSc != null) {
|
||||
mSc.cancel(true);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
/**
|
||||
* 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.openwebnet.handler;
|
||||
|
||||
import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.binding.openwebnet.handler.config.OpenWebNetBusBridgeConfig;
|
||||
import org.openhab.binding.openwebnet.handler.config.OpenWebNetZigBeeBridgeConfig;
|
||||
import org.openhab.binding.openwebnet.internal.discovery.OpenWebNetDeviceDiscoveryService;
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openwebnet4j.BUSGateway;
|
||||
import org.openwebnet4j.GatewayListener;
|
||||
import org.openwebnet4j.OpenDeviceType;
|
||||
import org.openwebnet4j.OpenGateway;
|
||||
import org.openwebnet4j.USBGateway;
|
||||
import org.openwebnet4j.communication.OWNAuthException;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.message.Automation;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.GatewayMgmt;
|
||||
import org.openwebnet4j.message.Lighting;
|
||||
import org.openwebnet4j.message.OpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetBridgeHandler} is responsible for handling communication with gateways and handling events.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implements GatewayListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetBridgeHandler.class);
|
||||
|
||||
private static final int GATEWAY_ONLINE_TIMEOUT_SEC = 20; // Time to wait for the gateway to become connected
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.BRIDGE_SUPPORTED_THING_TYPES;
|
||||
|
||||
// ConcurrentHashMap of devices registered to this BridgeHandler
|
||||
// association is: ownId (String) -> OpenWebNetThingHandler, with ownId = WHO.WHERE
|
||||
private Map<String, @Nullable OpenWebNetThingHandler> registeredDevices = new ConcurrentHashMap<>();
|
||||
|
||||
protected @Nullable OpenGateway gateway;
|
||||
private boolean isBusGateway = false;
|
||||
|
||||
private boolean isGatewayConnected = false;
|
||||
|
||||
public @Nullable OpenWebNetDeviceDiscoveryService deviceDiscoveryService;
|
||||
private boolean reconnecting = false; // we are trying to reconnect to gateway
|
||||
private boolean scanIsActive = false; // a device scan has been activated by OpenWebNetDeviceDiscoveryService;
|
||||
private boolean discoveryByActivation;
|
||||
|
||||
public OpenWebNetBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
public boolean isBusGateway() {
|
||||
return isBusGateway;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ThingTypeUID thingType = getThing().getThingTypeUID();
|
||||
OpenGateway gw;
|
||||
if (thingType.equals(THING_TYPE_ZB_GATEWAY)) {
|
||||
gw = initZigBeeGateway();
|
||||
} else {
|
||||
gw = initBusGateway();
|
||||
isBusGateway = true;
|
||||
}
|
||||
if (gw != null) {
|
||||
gateway = gw;
|
||||
gw.subscribe(this);
|
||||
if (gw.isConnected()) { // gateway is already connected, device can go ONLINE
|
||||
isGatewayConnected = true;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
logger.debug("Trying to connect gateway...");
|
||||
try {
|
||||
gw.connect();
|
||||
scheduler.schedule(() -> {
|
||||
// if status is still UNKNOWN after timer ends, set the device as OFFLINE
|
||||
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
|
||||
logger.info("status still UNKNOWN. Setting device={} to OFFLINE", thing.getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not connect to gateway before " + GATEWAY_ONLINE_TIMEOUT_SEC + "s");
|
||||
}
|
||||
}, GATEWAY_ONLINE_TIMEOUT_SEC, TimeUnit.SECONDS);
|
||||
} catch (OWNException e) {
|
||||
logger.debug("gw.connect() returned OWNException: {}", e.getMessage());
|
||||
// status is updated by callback onConnectionError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init a ZigBee gateway based on config
|
||||
*/
|
||||
private @Nullable OpenGateway initZigBeeGateway() {
|
||||
logger.debug("Initializing ZigBee USB gateway");
|
||||
OpenWebNetZigBeeBridgeConfig zbBridgeConfig = getConfigAs(OpenWebNetZigBeeBridgeConfig.class);
|
||||
String serialPort = zbBridgeConfig.getSerialPort();
|
||||
if (serialPort == null || serialPort.isEmpty()) {
|
||||
logger.warn("Cannot connect to gateway. No serial port has been provided in Bridge configuration.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-no-serial-port");
|
||||
return null;
|
||||
} else {
|
||||
return new USBGateway(serialPort);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init a BUS gateway based on config
|
||||
*/
|
||||
private @Nullable OpenGateway initBusGateway() {
|
||||
logger.debug("Initializing BUS gateway");
|
||||
OpenWebNetBusBridgeConfig busBridgeConfig = getConfigAs(OpenWebNetBusBridgeConfig.class);
|
||||
String host = busBridgeConfig.getHost();
|
||||
if (host == null || host.isEmpty()) {
|
||||
logger.warn("Cannot connect to gateway. No host/IP has been provided in Bridge configuration.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-no-ip-address");
|
||||
return null;
|
||||
} else {
|
||||
int port = busBridgeConfig.getPort().intValue();
|
||||
String passwd = busBridgeConfig.getPasswd();
|
||||
String passwdMasked;
|
||||
if (passwd.length() >= 4) {
|
||||
passwdMasked = "******" + passwd.substring(passwd.length() - 3, passwd.length());
|
||||
} else {
|
||||
passwdMasked = "******";
|
||||
}
|
||||
discoveryByActivation = busBridgeConfig.getDiscoveryByActivation();
|
||||
logger.debug("Creating new BUS gateway with config properties: {}:{}, pwd={}, discoveryByActivation={}",
|
||||
host, port, passwdMasked, discoveryByActivation);
|
||||
return new BUSGateway(host, port, passwd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand (command={} - channel={})", command, channelUID);
|
||||
OpenGateway gw = gateway;
|
||||
if (gw != null && !gw.isConnected()) {
|
||||
logger.warn("Gateway is NOT connected, skipping command");
|
||||
return;
|
||||
} else {
|
||||
logger.warn("Channel not supported: channel={}", channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigStatusMessage> getConfigStatus() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
disconnectGateway();
|
||||
super.handleRemoval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disconnectGateway();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void disconnectGateway() {
|
||||
OpenGateway gw = gateway;
|
||||
if (gw != null) {
|
||||
gw.closeConnection();
|
||||
gw.unsubscribe(this);
|
||||
logger.debug("gateway {} connection closed and unsubscribed", gw.toString());
|
||||
gateway = null;
|
||||
}
|
||||
reconnecting = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(OpenWebNetDeviceDiscoveryService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for devices connected to this bridge handler's gateway
|
||||
*
|
||||
* @param listener to receive device found notifications
|
||||
*/
|
||||
public synchronized void searchDevices() {
|
||||
scanIsActive = true;
|
||||
logger.debug("------$$ scanIsActive={}", scanIsActive);
|
||||
OpenGateway gw = gateway;
|
||||
if (gw != null) {
|
||||
if (!gw.isDiscovering()) {
|
||||
if (!gw.isConnected()) {
|
||||
logger.debug("------$$ Gateway is NOT connected, cannot search for devices");
|
||||
return;
|
||||
}
|
||||
logger.info("------$$ STARTED active SEARCH for devices on gateway '{}'", this.getThing().getUID());
|
||||
try {
|
||||
gw.discoverDevices();
|
||||
} catch (OWNException e) {
|
||||
logger.warn("------$$ OWNException while discovering devices on gateway {}: {}",
|
||||
this.getThing().getUID(), e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("------$$ Searching devices on gateway {} already activated", this.getThing().getUID());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.debug("------$$ Cannot search devices: no gateway associated to this handler");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewDevice(@Nullable Where w, @Nullable OpenDeviceType deviceType, @Nullable BaseOpenMessage message) {
|
||||
OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService;
|
||||
if (discService != null) {
|
||||
if (w != null && deviceType != null) {
|
||||
discService.newDiscoveryResult(w, deviceType, message);
|
||||
} else {
|
||||
logger.warn("onNewDevice with null where/deviceType, msg={}", message);
|
||||
}
|
||||
} else {
|
||||
logger.warn("onNewDevice but null deviceDiscoveryService");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiscoveryCompleted() {
|
||||
logger.info("------$$ FINISHED active SEARCH for devices on gateway '{}'", this.getThing().getUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that the scan has been stopped/aborted by OpenWebNetDeviceDiscoveryService
|
||||
*/
|
||||
public void scanStopped() {
|
||||
scanIsActive = false;
|
||||
logger.debug("------$$ scanIsActive={}", scanIsActive);
|
||||
}
|
||||
|
||||
private void discoverByActivation(BaseOpenMessage baseMsg) {
|
||||
logger.debug("BridgeHandler.discoverByActivation() msg={}", baseMsg);
|
||||
OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService;
|
||||
if (discService == null) {
|
||||
logger.warn("discoverByActivation: null OpenWebNetDeviceDiscoveryService, ignoring msg={}", baseMsg);
|
||||
return;
|
||||
}
|
||||
if (baseMsg instanceof Lighting) {
|
||||
OpenDeviceType type = null;
|
||||
try {
|
||||
type = baseMsg.detectDeviceType();
|
||||
} catch (FrameException e) {
|
||||
logger.warn("Exception while detecting device type: {}", e.getMessage());
|
||||
}
|
||||
if (type != null) {
|
||||
discService.newDiscoveryResult(baseMsg.getWhere(), type, baseMsg);
|
||||
} else {
|
||||
logger.debug("discoverByActivation: no device type detected from msg: {}", baseMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a device ThingHandler to this BridgHandler
|
||||
*
|
||||
* @param ownId the device OpenWebNet id
|
||||
* @param thingHandler the thing handler to be registered
|
||||
*/
|
||||
protected void registerDevice(String ownId, OpenWebNetThingHandler thingHandler) {
|
||||
if (registeredDevices.containsKey(ownId)) {
|
||||
logger.warn("registering device with an existing ownId={}", ownId);
|
||||
}
|
||||
registeredDevices.put(ownId, thingHandler);
|
||||
logger.debug("registered device ownId={}, thing={}", ownId, thingHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-register a device from this bridge handler
|
||||
*
|
||||
* @param oId the device OpenWebNet id
|
||||
*/
|
||||
protected void unregisterDevice(String oId) {
|
||||
if (registeredDevices.remove(oId) != null) {
|
||||
logger.debug("un-registered device ownId={}", oId);
|
||||
} else {
|
||||
logger.warn("could not un-register ownId={} (not found)", oId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEventMessage(@Nullable OpenMessage msg) {
|
||||
logger.trace("RECEIVED <<<<< {}", msg);
|
||||
if (msg == null) {
|
||||
logger.warn("received event msg is null");
|
||||
return;
|
||||
}
|
||||
if (msg.isACK() || msg.isNACK()) {
|
||||
return; // we ignore ACKS/NACKS
|
||||
}
|
||||
// GATEWAY MANAGEMENT
|
||||
if (msg instanceof GatewayMgmt) {
|
||||
// noop
|
||||
return;
|
||||
}
|
||||
|
||||
BaseOpenMessage baseMsg = (BaseOpenMessage) msg;
|
||||
// let's try to get the Thing associated with this message...
|
||||
if (baseMsg instanceof Lighting || baseMsg instanceof Automation) {
|
||||
String ownId = ownIdFromMessage(baseMsg);
|
||||
logger.debug("ownId={}", ownId);
|
||||
OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
|
||||
if (deviceHandler == null) {
|
||||
OpenGateway gw = gateway;
|
||||
if (isBusGateway && ((gw != null && !gw.isDiscovering() && scanIsActive)
|
||||
|| (discoveryByActivation && !scanIsActive))) {
|
||||
discoverByActivation(baseMsg);
|
||||
} else {
|
||||
logger.debug("ownId={} has NO DEVICE associated, ignoring it", ownId);
|
||||
}
|
||||
} else {
|
||||
deviceHandler.handleMessage(baseMsg);
|
||||
}
|
||||
} else {
|
||||
logger.debug("BridgeHandler ignoring frame {}. WHO={} is not supported by this binding", baseMsg,
|
||||
baseMsg.getWho());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
isGatewayConnected = true;
|
||||
Map<String, String> properties = editProperties();
|
||||
boolean propertiesChanged = false;
|
||||
OpenGateway gw = gateway;
|
||||
if (gw == null) {
|
||||
logger.warn("received onConnected() but gateway is null");
|
||||
return;
|
||||
}
|
||||
if (gw instanceof USBGateway) {
|
||||
logger.info("------------------- CONNECTED to USB (ZigBee) gateway - USB port: {}",
|
||||
((USBGateway) gw).getSerialPortName());
|
||||
} else {
|
||||
logger.info("------------------- CONNECTED to BUS gateway '{}' ({}:{})", thing.getUID(),
|
||||
((BUSGateway) gw).getHost(), ((BUSGateway) gw).getPort());
|
||||
// update serial number property (with MAC address)
|
||||
if (properties.get(PROPERTY_SERIAL_NO) != gw.getMACAddr().toUpperCase()) {
|
||||
properties.put(PROPERTY_SERIAL_NO, gw.getMACAddr().toUpperCase());
|
||||
propertiesChanged = true;
|
||||
logger.debug("updated property gw serialNumber: {}", properties.get(PROPERTY_SERIAL_NO));
|
||||
}
|
||||
}
|
||||
if (properties.get(PROPERTY_FIRMWARE_VERSION) != gw.getFirmwareVersion()) {
|
||||
properties.put(PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
|
||||
propertiesChanged = true;
|
||||
logger.debug("updated property gw firmware version: {}", properties.get(PROPERTY_FIRMWARE_VERSION));
|
||||
}
|
||||
if (propertiesChanged) {
|
||||
updateProperties(properties);
|
||||
logger.info("properties updated for '{}'", thing.getUID());
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionError(@Nullable OWNException error) {
|
||||
String errMsg;
|
||||
if (error == null) {
|
||||
errMsg = "unknown error";
|
||||
} else {
|
||||
errMsg = error.getMessage();
|
||||
}
|
||||
logger.info("------------------- ON CONNECTION ERROR: {}", errMsg);
|
||||
isGatewayConnected = false;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errMsg);
|
||||
tryReconnectGateway();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionClosed() {
|
||||
isGatewayConnected = false;
|
||||
logger.debug("onConnectionClosed() - isGatewayConnected={}", isGatewayConnected);
|
||||
// NOTE: cannot change to OFFLINE here because we are already in REMOVING state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(@Nullable OWNException e) {
|
||||
isGatewayConnected = false;
|
||||
String errMsg;
|
||||
if (e == null) {
|
||||
errMsg = "unknown error";
|
||||
} else {
|
||||
errMsg = e.getMessage();
|
||||
}
|
||||
logger.info("------------------- DISCONNECTED from gateway. OWNException={}", errMsg);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Disconnected from gateway (onDisconnected - " + errMsg + ")");
|
||||
tryReconnectGateway();
|
||||
}
|
||||
|
||||
private void tryReconnectGateway() {
|
||||
OpenGateway gw = gateway;
|
||||
if (gw != null) {
|
||||
if (!reconnecting) {
|
||||
reconnecting = true;
|
||||
logger.info("------------------- Starting RECONNECT cycle to gateway");
|
||||
try {
|
||||
gw.reconnect();
|
||||
} catch (OWNAuthException e) {
|
||||
logger.info("------------------- AUTH error from gateway. Stopping reconnect");
|
||||
reconnecting = false;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Authentication error. Check gateway password in Thing Configuration Parameters (" + e
|
||||
+ ")");
|
||||
}
|
||||
} else {
|
||||
logger.debug("------------------- reconnecting=true, do nothing");
|
||||
}
|
||||
} else {
|
||||
logger.debug("------------------- cannot start RECONNECT, gateway is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnected() {
|
||||
reconnecting = false;
|
||||
logger.info("------------------- RE-CONNECTED to gateway!");
|
||||
OpenGateway gw = gateway;
|
||||
if (gw != null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
if (gw.getFirmwareVersion() != null) {
|
||||
this.updateProperty(PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
|
||||
logger.debug("gw firmware version: {}", gw.getFirmwareVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ownId string (=WHO.WHERE) from a deviceWhere thing config parameter (already normalized) and its
|
||||
* handler.
|
||||
*
|
||||
* @param deviceWhere the device WHERE config parameter
|
||||
* @param handler the thing handler
|
||||
* @return the ownId
|
||||
*/
|
||||
protected String ownIdFromDeviceWhere(@Nullable String deviceWhere, OpenWebNetThingHandler handler) {
|
||||
return handler.ownIdPrefix() + "." + deviceWhere;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ownId string (=WHO.WHERE) from a Where address and Who
|
||||
*
|
||||
* @param where the Where address (to be normalized)
|
||||
* @param who the Who
|
||||
* @return the ownId
|
||||
*/
|
||||
public String ownIdFromWhoWhere(Where where, Who who) {
|
||||
return who.value() + "." + normalizeWhere(where);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ownId string (=WHO.WHERE) from a BaseOpenMessage
|
||||
*
|
||||
* @param baseMsg the BaseOpenMessage
|
||||
* @return the ownId String
|
||||
*/
|
||||
private String ownIdFromMessage(BaseOpenMessage baseMsg) {
|
||||
return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWhere());
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a Where address into a Thing id string based on bridge type (BUS/USB ZigBee).
|
||||
* '#' in WHERE are changed to 'h'
|
||||
*
|
||||
* @param where the Where address
|
||||
* @return the thing Id
|
||||
*/
|
||||
public String thingIdFromWhere(Where where) {
|
||||
return normalizeWhere(where).replace('#', 'h'); // '#' cannot be used in ThingUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a Where address for Thermo and Zigbee devices
|
||||
*
|
||||
* @param where the Where address
|
||||
* @return the normalized address
|
||||
*/
|
||||
public String normalizeWhere(Where where) {
|
||||
String str = "";
|
||||
if (isBusGateway) {
|
||||
if (where.value().indexOf('#') < 0) { // no hash present
|
||||
str = where.value();
|
||||
} else if (where.value().indexOf("#4#") > 0) { // local bus: APL#4#bus
|
||||
str = where.value();
|
||||
} else if (where.value().indexOf('#') == 0) { // thermo zone via central unit: #0 or #Z (Z=[1-99]) --> Z
|
||||
str = where.value().substring(1);
|
||||
} else if (where.value().indexOf('#') > 0) { // thermo zone and actuator N: Z#N (Z=[1-99], N=[1-9]) -- > Z
|
||||
str = where.value().substring(0, where.value().indexOf('#'));
|
||||
} else {
|
||||
logger.warn("normalizeWhere() unexpected WHERE: {}", where);
|
||||
str = where.value();
|
||||
}
|
||||
return str;
|
||||
} else {
|
||||
return where.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.openwebnet.handler;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetGenericHandler} is responsible for handling Generic OpenWebNet
|
||||
* devices. It does not too much, but it is needed to avoid handler errors and to tell the user
|
||||
* that some device has been found by the gateway but it was not recognised.
|
||||
* It extends the abstract {@link OpenWebNetThingHandler}.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetGenericHandler extends OpenWebNetThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetGenericHandler.class);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.GENERIC_SUPPORTED_THING_TYPES;
|
||||
|
||||
public OpenWebNetGenericHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestChannelState(ChannelUID channel) {
|
||||
// do nothing
|
||||
logger.warn("There are no channels");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleChannelCommand(ChannelUID channel, Command command) {
|
||||
// do nothing
|
||||
logger.warn("There are no channels");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String ownIdPrefix() {
|
||||
return "G";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
super.handleMessage(msg);
|
||||
// do nothing
|
||||
logger.warn("handleMessage(): Nothing to do!");
|
||||
}
|
||||
} // class
|
||||
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* 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.openwebnet.handler;
|
||||
|
||||
import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.Lighting;
|
||||
import org.openwebnet4j.message.What;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereZigBee;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetLightingHandler} is responsible for handling commands/messages for a Lighting OpenWebNet device.
|
||||
* It extends the abstract {@link OpenWebNetThingHandler}.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetLightingHandler.class);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.LIGHTING_SUPPORTED_THING_TYPES;
|
||||
|
||||
private static final int BRIGHTNESS_CHANGE_DELAY_MSEC = 1500; // delay to wait before sending another brightness
|
||||
// status request
|
||||
|
||||
private long lastBrightnessChangeSentTS = 0; // timestamp when last brightness change was sent to the device
|
||||
private boolean brightnessLevelRequested = false; // was the brightness level requested ?
|
||||
private int latestBrightnessWhat = -1; // latest brightness WHAT value (-1 = unknown)
|
||||
private int latestBrightnessWhatBeforeOff = -1; // latest brightness WHAT value before device was set to off
|
||||
|
||||
public OpenWebNetLightingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestChannelState(ChannelUID channel) {
|
||||
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
|
||||
try {
|
||||
send(Lighting.requestStatus(toWhere(channel)));
|
||||
} catch (OWNException e) {
|
||||
logger.warn("Exception while requesting channel {} state: {}", channel, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleChannelCommand(ChannelUID channel, Command command) {
|
||||
switch (channel.getId()) {
|
||||
case CHANNEL_BRIGHTNESS:
|
||||
handleBrightnessCommand(command);
|
||||
break;
|
||||
case CHANNEL_SWITCH:
|
||||
case CHANNEL_SWITCH_01:
|
||||
case CHANNEL_SWITCH_02:
|
||||
handleSwitchCommand(channel, command);
|
||||
break;
|
||||
default: {
|
||||
logger.warn("Unsupported channel UID {}", channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Lighting switch command for a channel
|
||||
*
|
||||
* @param channel the channel
|
||||
* @param command the command
|
||||
*/
|
||||
private void handleSwitchCommand(ChannelUID channel, Command command) {
|
||||
logger.debug("handleSwitchCommand() (command={} - channel={})", command, channel);
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
send(Lighting.requestTurnOn(toWhere(channel)));
|
||||
} else if (OnOffType.OFF.equals(command)) {
|
||||
send(Lighting.requestTurnOff(toWhere(channel)));
|
||||
}
|
||||
} catch (OWNException e) {
|
||||
logger.warn("Exception while processing command {}: {}", command, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unsupported command: {}", command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Lighting brightness command (ON, OFF, xx%, INCREASE, DECREASE)
|
||||
*
|
||||
* @param command the command
|
||||
*/
|
||||
private void handleBrightnessCommand(Command command) {
|
||||
logger.debug("handleBrightnessCommand() command={}", command);
|
||||
if (command instanceof PercentType) {
|
||||
int percent = ((PercentType) command).intValue();
|
||||
dimLightTo(Lighting.percentToWhat(percent).value(), command);
|
||||
} else if (command instanceof IncreaseDecreaseType) {
|
||||
if (IncreaseDecreaseType.INCREASE.equals(command)) {
|
||||
dimLightTo(latestBrightnessWhat + 1, command);
|
||||
} else { // DECREASE
|
||||
dimLightTo(latestBrightnessWhat - 1, command);
|
||||
}
|
||||
} else if (command instanceof OnOffType) {
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
dimLightTo(latestBrightnessWhat, command);
|
||||
} else { // OFF
|
||||
dimLightTo(0, command);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Cannot handle command {} for thing {}", command, getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to dim light to a valid OWN value
|
||||
*/
|
||||
private void dimLightTo(int whatInt, Command command) {
|
||||
int newWhatInt = whatInt;
|
||||
logger.debug("-DIM- dimLightTo() latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}",
|
||||
latestBrightnessWhat, latestBrightnessWhatBeforeOff, brightnessLevelRequested);
|
||||
What newWhat;
|
||||
if (OnOffType.ON.equals(command) && latestBrightnessWhat <= 0) {
|
||||
// ON after OFF/Unknown -> we reset channel to last value before OFF (if exists)
|
||||
if (latestBrightnessWhatBeforeOff > 0) { // we know last brightness -> set dimmer to it
|
||||
newWhatInt = latestBrightnessWhatBeforeOff;
|
||||
} else { // we do not know last brightness -> set dimmer to 100%
|
||||
newWhatInt = 10;
|
||||
}
|
||||
}
|
||||
logger.debug("-DIM- requested level={}", newWhatInt);
|
||||
if (newWhatInt != latestBrightnessWhat) {
|
||||
if (newWhatInt >= 0 && newWhatInt <= 10) {
|
||||
newWhat = Lighting.WHAT.fromValue(newWhatInt);
|
||||
if (newWhat.equals(Lighting.WHAT.ON)) {
|
||||
// change it to WHAT.DIMMER_20 (dimming to 10% is not allowed in OWN)
|
||||
newWhat = Lighting.WHAT.DIMMER_20;
|
||||
}
|
||||
// save current brightness level before sending bri=0 command to device
|
||||
if (newWhatInt == 0) {
|
||||
latestBrightnessWhatBeforeOff = latestBrightnessWhat;
|
||||
}
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
try {
|
||||
lastBrightnessChangeSentTS = System.currentTimeMillis();
|
||||
send(Lighting.requestDimTo(w.value(), newWhat));
|
||||
} catch (OWNException e) {
|
||||
logger.warn("Exception while sending dimLightTo for command {}: {}", command, e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("-DIM- do nothing");
|
||||
}
|
||||
} else {
|
||||
logger.debug("-DIM- do nothing");
|
||||
}
|
||||
logger.debug("-DIM- latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}", latestBrightnessWhat,
|
||||
latestBrightnessWhatBeforeOff, brightnessLevelRequested);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String ownIdPrefix() {
|
||||
return Who.LIGHTING.value().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
super.handleMessage(msg);
|
||||
updateLightState((Lighting) msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates light state based on a OWN Lighting event message received
|
||||
*
|
||||
* @param msg the Lighting message received
|
||||
*/
|
||||
private void updateLightState(Lighting msg) {
|
||||
logger.debug("updateLightState() for thing: {}", thing.getUID());
|
||||
ThingTypeUID thingType = thing.getThingTypeUID();
|
||||
if (THING_TYPE_ZB_DIMMER.equals(thingType) || THING_TYPE_BUS_DIMMER.equals(thingType)) {
|
||||
updateLightBrightnessState(msg);
|
||||
} else {
|
||||
updateLightOnOffState(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates on/off state based on a OWN Lighting event message received
|
||||
*
|
||||
* @param msg the Lighting message received
|
||||
*/
|
||||
private void updateLightOnOffState(Lighting msg) {
|
||||
String channelID;
|
||||
OpenWebNetBridgeHandler brH = bridgeHandler;
|
||||
if (brH != null) {
|
||||
if (brH.isBusGateway()) {
|
||||
channelID = CHANNEL_SWITCH;
|
||||
} else {
|
||||
WhereZigBee w = (WhereZigBee) (msg.getWhere());
|
||||
if (WhereZigBee.UNIT_02.equals(w.getUnit())) {
|
||||
channelID = CHANNEL_SWITCH_02;
|
||||
} else {
|
||||
channelID = CHANNEL_SWITCH_01;
|
||||
}
|
||||
}
|
||||
if (msg.isOn()) {
|
||||
updateState(channelID, OnOffType.ON);
|
||||
} else if (msg.isOff()) {
|
||||
updateState(channelID, OnOffType.OFF);
|
||||
} else {
|
||||
logger.debug("updateLightOnOffState() Ignoring unsupported WHAT for thing {}. Frame={}",
|
||||
getThing().getUID(), msg.getFrameValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates brightness level based on a OWN Lighting event message received
|
||||
*
|
||||
* @param msg the Lighting message received
|
||||
*/
|
||||
private synchronized void updateLightBrightnessState(Lighting msg) {
|
||||
final String channel = CHANNEL_BRIGHTNESS;
|
||||
logger.debug(" $BRI updateLightBrightnessState() msg={}", msg);
|
||||
logger.debug(" $BRI updateLightBr() latestBriWhat={} latestBriBeforeOff={} brightnessLevelRequested={}",
|
||||
latestBrightnessWhat, latestBrightnessWhatBeforeOff, brightnessLevelRequested);
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - lastBrightnessChangeSentTS;
|
||||
logger.debug(" $BRI now={} -> delta={}", now, delta);
|
||||
if (msg.isOn() && !brightnessLevelRequested) {
|
||||
if (delta >= BRIGHTNESS_CHANGE_DELAY_MSEC) {
|
||||
// we send a light brightness status request ONLY if last brightness change
|
||||
// was not just sent (>=BRIGHTNESS_CHANGE_DELAY_MSEC ago)
|
||||
logger.debug(" $BRI change sent >={}ms ago, sending requestStatus...", BRIGHTNESS_CHANGE_DELAY_MSEC);
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
try {
|
||||
send(Lighting.requestStatus(w.value()));
|
||||
brightnessLevelRequested = true;
|
||||
} catch (OWNException e) {
|
||||
logger.warn(" $BRI exception while requesting light state: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug(" $BRI change sent {}<{}ms, NO requestStatus needed", delta,
|
||||
BRIGHTNESS_CHANGE_DELAY_MSEC);
|
||||
}
|
||||
} else {
|
||||
logger.debug(" $BRI update from network -> level should be present in WHAT part of the message");
|
||||
if (msg.getWhat() != null) {
|
||||
int newLevel = msg.getWhat().value();
|
||||
logger.debug(" $BRI current level={} ----> new level={}", latestBrightnessWhat, newLevel);
|
||||
if (latestBrightnessWhat != newLevel) {
|
||||
updateState(channel, new PercentType(Lighting.levelToPercent(newLevel)));
|
||||
if (msg.isOff()) {
|
||||
latestBrightnessWhatBeforeOff = latestBrightnessWhat;
|
||||
}
|
||||
latestBrightnessWhat = newLevel;
|
||||
} else {
|
||||
logger.debug(" $BRI no change");
|
||||
}
|
||||
brightnessLevelRequested = false;
|
||||
} else { // dimension notification
|
||||
if (msg.getDim() == Lighting.DIM.DIMMER_LEVEL_100) {
|
||||
int newPercent;
|
||||
try {
|
||||
newPercent = msg.parseDimmerLevel100();
|
||||
} catch (FrameException fe) {
|
||||
logger.warn("updateLightBrightnessState() Wrong value for dimmerLevel100 in message: {}", msg);
|
||||
return;
|
||||
}
|
||||
int newLevel = Lighting.percentToWhat(newPercent).value();
|
||||
logger.debug(" $BRI latest level={} ----> new percent={} ----> new level={}", latestBrightnessWhat,
|
||||
newPercent, newLevel);
|
||||
updateState(channel, new PercentType(newPercent));
|
||||
if (newPercent == 0) {
|
||||
latestBrightnessWhatBeforeOff = latestBrightnessWhat;
|
||||
}
|
||||
latestBrightnessWhat = newLevel;
|
||||
brightnessLevelRequested = false;
|
||||
} else {
|
||||
logger.warn("updateLightBrightnessState() Cannot handle message {} for thing {}", msg,
|
||||
getThing().getUID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug(" $BRI latestBriWhat={} latestBriBeforeOff={} brightnessLevelRequested={}", latestBrightnessWhat,
|
||||
latestBrightnessWhatBeforeOff, brightnessLevelRequested);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a WHERE address string based on bridge type and unit (optional)
|
||||
*
|
||||
* @param unit the device unit
|
||||
**/
|
||||
@Nullable
|
||||
protected String toWhere(String unit) {
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
OpenWebNetBridgeHandler brH = bridgeHandler;
|
||||
if (brH != null && brH.isBusGateway()) {
|
||||
return w.value();
|
||||
} else {
|
||||
return w + unit;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a WHERE address string based on channel
|
||||
*
|
||||
* @param channel the channel
|
||||
**/
|
||||
@Nullable
|
||||
protected String toWhere(ChannelUID channel) {
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
OpenWebNetBridgeHandler brH = bridgeHandler;
|
||||
if (brH != null) {
|
||||
if (brH.isBusGateway()) {
|
||||
return w.value();
|
||||
} else if (channel.getId().equals(CHANNEL_SWITCH_02)) {
|
||||
return ((WhereZigBee) w).valueWithUnit(WhereZigBee.UNIT_02);
|
||||
} else { // CHANNEL_SWITCH_01 or other channels
|
||||
return ((WhereZigBee) w).valueWithUnit(WhereZigBee.UNIT_01);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* 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.openwebnet.handler;
|
||||
|
||||
import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openwebnet4j.OpenGateway;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.communication.Response;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.OpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereLightAutom;
|
||||
import org.openwebnet4j.message.WhereZigBee;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetThingHandler} is responsible for handling commands for a 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
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class OpenWebNetThingHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetThingHandler.class);
|
||||
|
||||
protected @Nullable OpenWebNetBridgeHandler bridgeHandler;
|
||||
protected @Nullable String ownId; // OpenWebNet identifier for this device: WHO.WHERE
|
||||
protected @Nullable Where deviceWhere; // this device Where address
|
||||
|
||||
protected @Nullable ScheduledFuture<?> refreshTimeout;
|
||||
|
||||
public OpenWebNetThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
OpenWebNetBridgeHandler brH = (OpenWebNetBridgeHandler) bridge.getHandler();
|
||||
if (brH != null) {
|
||||
bridgeHandler = brH;
|
||||
Object deviceWhereConfig = getConfig().get(CONFIG_PROPERTY_WHERE);
|
||||
if (!(deviceWhereConfig instanceof String)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"WHERE parameter in configuration is null or invalid");
|
||||
return;
|
||||
} else {
|
||||
String deviceWhereStr = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
|
||||
Where w;
|
||||
if (brH.isBusGateway()) {
|
||||
w = new WhereLightAutom(deviceWhereStr);
|
||||
} else {
|
||||
w = new WhereZigBee(deviceWhereStr);
|
||||
}
|
||||
deviceWhere = w;
|
||||
final String oid = brH.ownIdFromDeviceWhere(w.value(), this);
|
||||
ownId = oid;
|
||||
Map<String, String> properties = editProperties();
|
||||
properties.put(PROPERTY_OWNID, oid);
|
||||
updateProperties(properties);
|
||||
brH.registerDevice(oid, this);
|
||||
logger.debug("associated thing to bridge with ownId={}", ownId);
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "waiting state update...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"No bridge associated, please assign a bridge in thing configuration.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channel, Command command) {
|
||||
logger.debug("handleCommand() (command={} - channel={})", command, channel);
|
||||
OpenWebNetBridgeHandler handler = bridgeHandler;
|
||||
if (handler != null) {
|
||||
OpenGateway gw = handler.gateway;
|
||||
if (gw != null && !gw.isConnected()) {
|
||||
logger.info("Cannot handle command {}:{} for {}: gateway is not connected", channel, command,
|
||||
getThing().getUID());
|
||||
return;
|
||||
}
|
||||
if (command instanceof RefreshType) {
|
||||
requestChannelState(channel);
|
||||
// set a schedule to put device OFFLINE if no answer is received after THING_STATE_REQ_TIMEOUT_SEC
|
||||
refreshTimeout = scheduler.schedule(() -> {
|
||||
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not get channel state (timer expired)");
|
||||
}
|
||||
}, THING_STATE_REQ_TIMEOUT_SEC, TimeUnit.SECONDS);
|
||||
} else {
|
||||
handleChannelCommand(channel, command);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Thing {} is not associated to any gateway, skipping command", getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a command for the specific channel for this thing.
|
||||
* It must be implemented by each specific OpenWebNet category of device (WHO), based on channel
|
||||
*
|
||||
* @param channel specific ChannleUID
|
||||
* @param command the Command to be executed
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param msg the message to handle
|
||||
*/
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
ThingStatus ts = getThing().getStatus();
|
||||
if (ThingStatus.ONLINE != ts && ThingStatus.REMOVING != ts && ThingStatus.REMOVED != ts) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to send OWN messages from ThingsHandlers
|
||||
*/
|
||||
protected @Nullable Response send(OpenMessage msg) throws OWNException {
|
||||
OpenWebNetBridgeHandler handler = bridgeHandler;
|
||||
if (handler != null) {
|
||||
OpenGateway gw = handler.gateway;
|
||||
if (gw != null) {
|
||||
return gw.send(msg);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to send with high priority OWN messages from ThingsHandlers
|
||||
*/
|
||||
protected @Nullable Response sendHighPriority(OpenMessage msg) throws OWNException {
|
||||
OpenWebNetBridgeHandler handler = bridgeHandler;
|
||||
if (handler != null) {
|
||||
OpenGateway gw = handler.gateway;
|
||||
if (gw != null) {
|
||||
return gw.sendHighPriority(msg);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to gateway state for thing channel. It must be implemented by each specific device handler.
|
||||
*
|
||||
* @param channel the channel to request the state for
|
||||
*/
|
||||
protected abstract void requestChannelState(ChannelUID channel);
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
OpenWebNetBridgeHandler bh = bridgeHandler;
|
||||
String oid = ownId;
|
||||
if (bh != null && oid != null) {
|
||||
bh.unregisterDevice(oid);
|
||||
}
|
||||
ScheduledFuture<?> sc = refreshTimeout;
|
||||
if (sc != null) {
|
||||
sc.cancel(true);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prefix String for ownId specific for each handler. To be implemented by sub-classes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract String ownIdPrefix();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.openwebnet.handler.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* BUS Bridge configuration object
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetBusBridgeConfig {
|
||||
|
||||
private BigDecimal port = new BigDecimal(20000);
|
||||
private @Nullable String host;
|
||||
private String passwd = "12345";
|
||||
private boolean discoveryByActivation = false;
|
||||
|
||||
public BigDecimal getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public @Nullable String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getPasswd() {
|
||||
return passwd;
|
||||
}
|
||||
|
||||
public Boolean getDiscoveryByActivation() {
|
||||
return discoveryByActivation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.openwebnet.handler.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ZigBee USB Bridge configuration object
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetZigBeeBridgeConfig {
|
||||
|
||||
private @Nullable String serialPort;
|
||||
|
||||
public @Nullable String getSerialPort() {
|
||||
return serialPort;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.openwebnet.internal;
|
||||
|
||||
import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.ALL_SUPPORTED_THING_TYPES;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.handler.OpenWebNetAutomationHandler;
|
||||
import org.openhab.binding.openwebnet.handler.OpenWebNetBridgeHandler;
|
||||
import org.openhab.binding.openwebnet.handler.OpenWebNetGenericHandler;
|
||||
import org.openhab.binding.openwebnet.handler.OpenWebNetLightingHandler;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetHandlerFactory} is responsible for creating thing handlers.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.openwebnet", service = ThingHandlerFactory.class)
|
||||
public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetHandlerFactory.class);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return ALL_SUPPORTED_THING_TYPES.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
if (OpenWebNetBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW BRIDGE Handler");
|
||||
return new OpenWebNetBridgeHandler((Bridge) thing);
|
||||
} else if (OpenWebNetGenericHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW GENERIC Handler");
|
||||
return new OpenWebNetGenericHandler(thing);
|
||||
} else if (OpenWebNetLightingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW LIGHTING Handler");
|
||||
return new OpenWebNetLightingHandler(thing);
|
||||
} else if (OpenWebNetAutomationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW AUTOMATION Handler");
|
||||
return new OpenWebNetAutomationHandler(thing);
|
||||
}
|
||||
logger.warn("ThingType {} is not supported by this binding", thing.getThingTypeUID());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* 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.openwebnet.internal.discovery;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.jupnp.model.meta.DeviceDetails;
|
||||
import org.jupnp.model.meta.ManufacturerDetails;
|
||||
import org.jupnp.model.meta.ModelDetails;
|
||||
import org.jupnp.model.meta.RemoteDevice;
|
||||
import org.jupnp.model.meta.RemoteDeviceIdentity;
|
||||
import org.jupnp.model.types.UDN;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BusGatewayUpnpDiscovery} is responsible for discovering supported BTicino BUS
|
||||
* gateways devices using UPnP. It implements {@link UpnpDiscoveryParticipant}.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = UpnpDiscoveryParticipant.class)
|
||||
public class BusGatewayUpnpDiscovery implements UpnpDiscoveryParticipant {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BusGatewayUpnpDiscovery.class);
|
||||
|
||||
public enum BusGatewayId {
|
||||
MH201("IPscenarioModule", "MH201"),
|
||||
MH202("scheduler", "MH202"),
|
||||
F454("webserver", "F454"),
|
||||
MY_HOME_SERVER1("myhomeserver1", "MYHOMESERVER1"),
|
||||
TOUCH_SCREEN_10("ts10", "TOUCHSCREEN10"),
|
||||
MH200N("lightingcontrolunit", "MH200N");
|
||||
|
||||
private final String value, thingId;
|
||||
|
||||
private BusGatewayId(String value, String thingId) {
|
||||
this.value = value;
|
||||
this.thingId = thingId;
|
||||
}
|
||||
|
||||
public static @Nullable BusGatewayId fromValue(String s) {
|
||||
Optional<BusGatewayId> m = Arrays.stream(values()).filter(val -> s.equals(val.value)).findFirst();
|
||||
if (m.isPresent()) {
|
||||
return m.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getThingId() {
|
||||
return thingId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DeviceInfo bean to store device useful info (and log them)
|
||||
*/
|
||||
public class DeviceInfo {
|
||||
@Nullable
|
||||
private String friendlyName;
|
||||
private String modelName = "<unknown>";
|
||||
private String modelDescription = "<unknown>";
|
||||
private String modelNumber = "<unknown>";
|
||||
private String serialNumber = "<unknown>";
|
||||
@Nullable
|
||||
private String host;
|
||||
private String manufacturer = "<unknown>";
|
||||
@Nullable
|
||||
private UDN udn;
|
||||
private boolean isBTicino = false;
|
||||
|
||||
private DeviceInfo(RemoteDevice device) {
|
||||
String deviceLog = "Discovered device:\n+=== UPnP =========================================";
|
||||
RemoteDeviceIdentity identity = device.getIdentity();
|
||||
if (identity != null) {
|
||||
this.udn = identity.getUdn();
|
||||
deviceLog += "\n| ID.UDN : " + udn;
|
||||
if (identity.getDescriptorURL() != null) {
|
||||
deviceLog += "\n| ID.DESC URL : " + identity.getDescriptorURL();
|
||||
this.host = identity.getDescriptorURL().getHost();
|
||||
}
|
||||
deviceLog += "\n| ID.MAX AGE : " + identity.getMaxAgeSeconds();
|
||||
}
|
||||
deviceLog += "\n| --------------";
|
||||
DeviceDetails details = device.getDetails();
|
||||
if (details != null) {
|
||||
ManufacturerDetails manufacturerDetails = details.getManufacturerDetails();
|
||||
if (manufacturerDetails != null) {
|
||||
this.manufacturer = manufacturerDetails.getManufacturer();
|
||||
deviceLog += "\n| MANUFACTURER : " + manufacturer + " (" + manufacturerDetails.getManufacturerURI()
|
||||
+ ")";
|
||||
if (manufacturer.toUpperCase().contains("BTICINO")) {
|
||||
this.isBTicino = true;
|
||||
}
|
||||
}
|
||||
ModelDetails modelDetails = details.getModelDetails();
|
||||
if (modelDetails != null) {
|
||||
// Model Name | Desc | Number (Uri)
|
||||
this.modelName = modelDetails.getModelName();
|
||||
this.modelDescription = modelDetails.getModelDescription();
|
||||
this.modelNumber = modelDetails.getModelNumber();
|
||||
deviceLog += "\n| MODEL : " + modelName + " | " + modelDescription + " | " + modelNumber
|
||||
+ " (" + modelDetails.getModelURI() + ")";
|
||||
}
|
||||
if (isBTicino) {
|
||||
this.friendlyName = details.getFriendlyName();
|
||||
deviceLog += "\n| FRIENDLY NAME: " + friendlyName;
|
||||
this.serialNumber = details.getSerialNumber();
|
||||
deviceLog += "\n| SERIAL # : " + serialNumber;
|
||||
deviceLog += "\n| BASE URL : " + details.getBaseURL();
|
||||
deviceLog += "\n| UPC : " + details.getUpc();
|
||||
}
|
||||
}
|
||||
deviceLog += "\n+==================================================";
|
||||
logger.debug(deviceLog);
|
||||
}
|
||||
} /* DeviceInfo */
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.singleton(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(RemoteDevice device) {
|
||||
logger.info("Found device {}", device.getType());
|
||||
DeviceInfo devInfo = new DeviceInfo(device);
|
||||
if (!devInfo.manufacturer.matches("<unknown>")) {
|
||||
logger.info(" |- {} ({})", devInfo.modelName, devInfo.manufacturer);
|
||||
}
|
||||
ThingUID thingId = generateThingUID(devInfo);
|
||||
if (thingId != null) {
|
||||
String host = devInfo.host;
|
||||
if (host != null) {
|
||||
String label = "BUS Gateway";
|
||||
String fn = devInfo.friendlyName;
|
||||
if (fn != null) {
|
||||
if (!fn.isEmpty()) {
|
||||
label = fn;
|
||||
}
|
||||
}
|
||||
label = label + " (" + devInfo.modelName + ", " + devInfo.modelNumber + ", " + devInfo.host + ")";
|
||||
Map<String, Object> properties = new HashMap<>(4);
|
||||
properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_HOST, host);
|
||||
properties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, devInfo.modelNumber);
|
||||
properties.put(OpenWebNetBindingConstants.PROPERTY_MODEL, devInfo.modelName);
|
||||
properties.put(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO, devInfo.serialNumber);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingId).withProperties(properties)
|
||||
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO).withLabel(label)
|
||||
.build();
|
||||
UDN udn = devInfo.udn;
|
||||
String udnStr;
|
||||
if (udn != null) {
|
||||
udnStr = udn.getIdentifierString();
|
||||
} else {
|
||||
udnStr = null;
|
||||
}
|
||||
logger.info("Created a DiscoveryResult for gateway '{}' (UDN={})", devInfo.friendlyName, udnStr);
|
||||
return result;
|
||||
} else {
|
||||
logger.warn("Could not get host for device (UDN={})", devInfo.udn);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(RemoteDevice device) {
|
||||
return generateThingUID(new DeviceInfo(device));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ThingUID for supported devices from already extracted DeviceInfo
|
||||
*
|
||||
* @param devInfo the device info
|
||||
* @return a new ThingUID, or null if the device is not supported by the binding
|
||||
*/
|
||||
private @Nullable ThingUID generateThingUID(DeviceInfo devInfo) {
|
||||
if (devInfo.isBTicino) {
|
||||
UDN udn = devInfo.udn;
|
||||
String idString = null;
|
||||
if (udn != null) {
|
||||
idString = udn.getIdentifierString();
|
||||
if (idString != null) {
|
||||
String[] spl = idString.split("-");
|
||||
if (spl.length > 3) {
|
||||
BusGatewayId gwId = BusGatewayId.fromValue(spl[1]);
|
||||
if (gwId != null) {
|
||||
logger.debug("'{}' is a supported gateway", gwId);
|
||||
String mac = spl[3]; // extract MAC address
|
||||
String normalizedMac = mac.toLowerCase().replaceAll("[^a-f0-9]", "");
|
||||
if (!normalizedMac.isEmpty()) {
|
||||
return new ThingUID(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY,
|
||||
gwId.getThingId() + "_" + normalizedMac);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("Found BTicino device: not a OpenWebNet gateway or is not supported (UDN={})", idString);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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.openwebnet.internal.discovery;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.binding.openwebnet.handler.OpenWebNetBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openwebnet4j.OpenDeviceType;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereZigBee;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering OpenWebNet devices connected to a
|
||||
* bridge/gateway
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
|
||||
implements DiscoveryService, ThingHandlerService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetDeviceDiscoveryService.class);
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.DEVICE_SUPPORTED_THING_TYPES;
|
||||
private static final int SEARCH_TIME_SEC = 60;
|
||||
|
||||
private @NonNullByDefault({}) OpenWebNetBridgeHandler bridgeHandler;
|
||||
private @NonNullByDefault({}) ThingUID bridgeUID;
|
||||
|
||||
public OpenWebNetDeviceDiscoveryService() {
|
||||
super(SUPPORTED_THING_TYPES, SEARCH_TIME_SEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return OpenWebNetDeviceDiscoveryService.SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.info("------ SEARCHING for DEVICES on bridge '{}' ({}) ...", bridgeHandler.getThing().getLabel(),
|
||||
bridgeUID);
|
||||
bridgeHandler.searchDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
logger.debug("------ stopScan() on bridge '{}'", bridgeUID);
|
||||
bridgeHandler.scanStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortScan() {
|
||||
logger.debug("------ abortScan() on bridge '{}'", bridgeUID);
|
||||
bridgeHandler.scanStopped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and notify to Inbox a new DiscoveryResult based on WHERE, OpenDeviceType and BaseOpenMessage
|
||||
*
|
||||
* @param where the discovered device's address (WHERE)
|
||||
* @param deviceType {@link OpenDeviceType} of the discovered device
|
||||
* @param message the OWN message received that identified the device (optional)
|
||||
*/
|
||||
public void newDiscoveryResult(Where where, OpenDeviceType deviceType, @Nullable BaseOpenMessage baseMsg) {
|
||||
logger.info("newDiscoveryResult() WHERE={}, deviceType={}", where, deviceType);
|
||||
ThingTypeUID thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE; // generic device
|
||||
String thingLabel = OpenWebNetBindingConstants.THING_LABEL_GENERIC_DEVICE;
|
||||
Who deviceWho = Who.UNKNOWN;
|
||||
switch (deviceType) {
|
||||
case ZIGBEE_ON_OFF_SWITCH:
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH;
|
||||
deviceWho = Who.LIGHTING;
|
||||
break;
|
||||
case ZIGBEE_DIMMER_SWITCH:
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_DIMMER;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_DIMMER;
|
||||
deviceWho = Who.LIGHTING;
|
||||
break;
|
||||
case SCS_ON_OFF_SWITCH:
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ON_OFF_SWITCH;
|
||||
deviceWho = Who.LIGHTING;
|
||||
break;
|
||||
case SCS_DIMMER_SWITCH:
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_DIMMER;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_DIMMER;
|
||||
deviceWho = Who.LIGHTING;
|
||||
break;
|
||||
case SCS_SHUTTER_SWITCH:
|
||||
case SCS_SHUTTER_CONTROL: {
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_AUTOMATION;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_AUTOMATION;
|
||||
deviceWho = Who.AUTOMATION;
|
||||
break;
|
||||
}
|
||||
case ZIGBEE_SHUTTER_SWITCH:
|
||||
case ZIGBEE_SHUTTER_CONTROL: {
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_AUTOMATION;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_AUTOMATION;
|
||||
deviceWho = Who.AUTOMATION;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logger.warn("Device type {} is not supported, default to GENERIC device (WHERE={})", deviceType, where);
|
||||
if (where instanceof WhereZigBee) {
|
||||
thingLabel = "ZigBee " + thingLabel;
|
||||
}
|
||||
if (baseMsg != null) {
|
||||
deviceWho = baseMsg.getWho();
|
||||
}
|
||||
}
|
||||
String tId = bridgeHandler.thingIdFromWhere(where);
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
|
||||
|
||||
DiscoveryResult discoveryResult = null;
|
||||
|
||||
String whereLabel = where.value();
|
||||
if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) {
|
||||
logger.debug("UNIT=02 found (WHERE={})", where);
|
||||
logger.debug("will remove previous result if exists");
|
||||
thingRemoved(thingUID); // remove previously discovered thing
|
||||
// re-create thingUID with new type
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS;
|
||||
thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
|
||||
whereLabel = whereLabel.replace("02#", "00#"); // replace unit '02' with all unit '00'
|
||||
logger.debug("UNIT=02, switching type from {} to {}",
|
||||
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH,
|
||||
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS);
|
||||
}
|
||||
Map<String, Object> properties = new HashMap<>(2);
|
||||
properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, bridgeHandler.normalizeWhere(where));
|
||||
properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, bridgeHandler.ownIdFromWhoWhere(where, deviceWho));
|
||||
if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE) {
|
||||
thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereLabel + ")";
|
||||
} else {
|
||||
thingLabel = thingLabel + " (WHERE=" + whereLabel + ")";
|
||||
}
|
||||
discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withProperties(properties)
|
||||
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)
|
||||
.withLabel(thingLabel).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof OpenWebNetBridgeHandler) {
|
||||
logger.debug("attaching {} to handler {} ", this, handler);
|
||||
bridgeHandler = (OpenWebNetBridgeHandler) handler;
|
||||
bridgeHandler.deviceDiscoveryService = this;
|
||||
bridgeUID = bridgeHandler.getThing().getUID();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return bridgeHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="openwebnet" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>OpenWebNet (BTicino/Legrand) Binding</name>
|
||||
<description>The OpenWebNet Binding integrates the BTicino/Legrand 'MyHOME' connected home system using the OpenWebNet
|
||||
protocol. It supports BUS (SCS) and ZigBee USB gateways and devices.</description>
|
||||
<author>Massimo Valla</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,4 @@
|
||||
# Thing status descriptions
|
||||
offline.conf-error-no-ip-address = Cannot connect to gateway. No host/IP has been provided in Bridge configuration.
|
||||
offline.conf-error-no-serial-port = Cannot connect to gateway. No serial port has been provided in Bridge configuration.
|
||||
offline.wrong-configuration = Invalid configuration. Check Thing configuration parameters.
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for BUS Automation (BTicino xxx/xxx/...) -->
|
||||
<thing-type id="bus_automation">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Automation</label>
|
||||
<description>A OpenWebNet BUS/SCS automation device to control roller shutters, blinds, etc. BTicino models:
|
||||
xxx/yyyy/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="shutter" typeId="shutter"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-xxxx/yyyy/etc.</property>
|
||||
<property name="ownDeviceType">514</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="shutterRun" type="text" required="true">
|
||||
<label>Shutter Run</label>
|
||||
<description>Time (in ms) to go from max position (e.g. CLOSED) to the other position (e.g. OPEN). Example: 12000
|
||||
(=12sec).
|
||||
Use AUTO (default) to calibrate the shutter automatically (UP->DOWN->Position%) the first time a Position
|
||||
command (%)
|
||||
is sent.</description>
|
||||
<default>AUTO</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (WHERE)</label>
|
||||
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for Dimmer (BTicino yyyy/zzzz/...) -->
|
||||
<thing-type id="bus_dimmer">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Dimmer</label>
|
||||
<description>A OpenWebNet BUS/SCS dimmer for the dimmer control of 1 light. BTicino models: yyyy/zzzz/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="brightness" typeId="brightness"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-yyyy/zzzz/etc.</property>
|
||||
<property name="ownDeviceType">258</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (WHERE)</label>
|
||||
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- OpenWebNet BUS gateway -->
|
||||
<bridge-type id="bus_gateway">
|
||||
<label>BUS Gateway</label>
|
||||
<description>This thing allows to connect to a IP BUS/SCS gateway that supports the OpenWebNet protocol (models: F454,
|
||||
MyHOMEServer1, F455, MH200N, F453, etc.)</description>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino</property>
|
||||
<property name="model">Unknown</property>
|
||||
<property name="firmwareVersion">Unknown</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>serialNumber</representation-property>
|
||||
|
||||
<config-description>
|
||||
|
||||
<parameter name="host" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Host</label>
|
||||
<description>OpenWebNet gateway IP address / hostname (example: 192.168.1.35)</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="port" type="integer" min="1" max="65535">
|
||||
<label>Port</label>
|
||||
<description>OpenWebNet gateway port (default: 20000)</description>
|
||||
<default>20000</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="passwd" type="text">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>OpenWebNet gateway password (default: 12345)</description>
|
||||
<default>12345</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="discoveryByActivation" type="boolean">
|
||||
<label>Discovery By Activation</label>
|
||||
<description>Discover BUS devices when they are activated (also when a device scan is not active) (default: false)</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for BUS On Off Switch (BTicino xxx/xxx/...) -->
|
||||
<thing-type id="bus_on_off_switch">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Switch</label>
|
||||
<description>A OpenWebNet BUS/SCS switch for the control of 1 light/load. BTicino models: xxx/yyyy/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-xxxx/yyyy/etc.</property>
|
||||
<property name="ownDeviceType">261</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (WHERE)</label>
|
||||
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- OpenWebNet Generic Device -->
|
||||
<thing-type id="device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
<bridge-type-ref id="zb_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Generic Device</label>
|
||||
<description>An OpenWebNet Generic Device.</description>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">Unknown</property>
|
||||
<property name="ownDeviceType">0</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (WHERE)</label>
|
||||
<description>It identifies one OpenWebNet device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for Automation (BTicino xxx/xxx/...) -->
|
||||
<thing-type id="zb_automation">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zb_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>ZigBee Automation</label>
|
||||
<description>A OpenWebNet ZigBee automation device to control roller shutters, blinds, etc. BTicino models:
|
||||
xxx/yyyy/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="shutter" typeId="shutter"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-xxxx/yyyy/etc.</property>
|
||||
<property name="ownDeviceType">512</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="shutterRun" type="text">
|
||||
<label>Shutter Run</label>
|
||||
<description>Time (in ms) to go from max position (e.g. CLOSED) to the other position (e.g. OPEN). Example: 12000
|
||||
(=12sec).
|
||||
Use AUTO (default) to calibrate the shutter automatically (UP->DOWN->Position%) the first time a Position
|
||||
command (%)
|
||||
is sent.</description>
|
||||
<required>true</required>
|
||||
<default>AUTO</default>
|
||||
</parameter>
|
||||
<parameter name="where" type="text">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee
|
||||
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for ZigBee Dimmer (BTicino 4585/4594/...) -->
|
||||
<thing-type id="zb_dimmer">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zb_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>ZigBee Dimmer</label>
|
||||
<description>A OpenWebNet ZigBee dimmer for the dimmer control of 1 light. BTicino models: 4585/4594/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="brightness" typeId="brightness"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-4585/4594/etc.</property>
|
||||
<property name="ownDeviceType">258</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee
|
||||
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- OpenWebNet ZigBee USB Gateway -->
|
||||
<bridge-type id="zb_gateway">
|
||||
<label>ZigBee USB Gateway</label>
|
||||
<description>This USB gateway (BTicino/Legrand models: BTI-3578/088328) connects to a BTicino/Legrand ZigBee network
|
||||
and uses the OpenWebNet protocol. For more information see: https://catalogo.bticino.it/BTI-3578-IT and
|
||||
https://www.legrand.com/ecatalogue/088328-openweb-net-zigbee-gateway-radio-interface.html</description>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-3578/088328</property>
|
||||
<property name="firmwareVersion">Unknown</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>zigbeeid</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="serialPort" type="text" required="true">
|
||||
<context>serial-port</context>
|
||||
<label>Serial Port</label>
|
||||
<description>Serial port where the OpenWebNet ZigBee USB Gateway is connected. Example: COM3 (Win), /dev/ttyUSB0
|
||||
(Linux), etc.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for OnOff Switch (BTicino 4591/3584/...) -->
|
||||
<thing-type id="zb_on_off_switch">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zb_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>ZigBee Switch</label>
|
||||
<description>A OpenWebNet ZigBee switch (actuator) for the control of 1 load/light. BTicino models: 4591/3684/etc.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="switch_01" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-4591/3684/etc.</property>
|
||||
<property name="ownDeviceType">256</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee
|
||||
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Thing for ZigBee OnOff Switch with 2 units (BTicino 4592) -->
|
||||
<thing-type id="zb_on_off_switch2u">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zb_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>ZigBee 2-units Switch</label>
|
||||
<description>A OpenWebNet ZigBee 2-units switch (actuator) for the control of 2 loads/lights. BTicino model: 4592</description>
|
||||
|
||||
<channels>
|
||||
<channel id="switch_01" typeId="switch"/>
|
||||
<channel id="switch_02" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-4592</property>
|
||||
<property name="ownDeviceType">256</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee
|
||||
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openwebnet"
|
||||
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">
|
||||
|
||||
<!-- Switch Channel -->
|
||||
<channel-type id="switch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Switch</label>
|
||||
<description>Switch the power ON and OFF</description>
|
||||
<category>Light</category>
|
||||
<tags>
|
||||
<tag>Lighting</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<!-- Brightness Channel -->
|
||||
<channel-type id="brightness">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Brightness</label>
|
||||
<description>Control the brightness and switch the light ON and OFF</description>
|
||||
<category>DimmableLight</category>
|
||||
<tags>
|
||||
<tag>Lighting</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<!-- Shutter Channel -->
|
||||
<channel-type id="shutter">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Roller shutter</label>
|
||||
<description>Control the roller shutter position</description>
|
||||
<category>Blinds</category>
|
||||
<tags>
|
||||
<tag>Blinds</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user