[openwebnet] added support for CEN/CEN+ scenarios (WHO=15/25) (#11398)
* [openwebnet] first support for CEN Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] added CEN actions. OpenWebNetThingHandler.send() is now public Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] added CEN+ support Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] use WhereCEN, removed nullpointer warnings from EnergyHandler. Improved README Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] completed support for CEN/CEN+ Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] improved log Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] corrected "pressure" and renamed some labels Signed-off-by: Massimo Valla <mvcode00@gmail.com>
This commit is contained in:
@@ -59,6 +59,12 @@ public class OpenWebNetBindingConstants {
|
||||
public static final String THING_LABEL_BUS_THERMO_SENSOR = "Thermo Sensor";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_THERMO_ZONE = new ThingTypeUID(BINDING_ID, "bus_thermo_zone");
|
||||
public static final String THING_LABEL_BUS_THERMO_ZONE = "Thermo Zone";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_CEN_SCENARIO_CONTROL = new ThingTypeUID(BINDING_ID,
|
||||
"bus_cen_scenario_control");
|
||||
public static final String THING_LABEL_BUS_CEN_SCENARIO_CONTROL = "CEN Control";
|
||||
public static final ThingTypeUID THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL = new ThingTypeUID(BINDING_ID,
|
||||
"bus_cenplus_scenario_control");
|
||||
public static final String THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL = "CEN+ Control";
|
||||
|
||||
// ZIGBEE
|
||||
public static final ThingTypeUID THING_TYPE_ZB_ON_OFF_SWITCH = new ThingTypeUID(BINDING_ID, "zb_on_off_switch");
|
||||
@@ -81,24 +87,22 @@ public class OpenWebNetBindingConstants {
|
||||
// ## Automation
|
||||
public static final Set<ThingTypeUID> AUTOMATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_AUTOMATION,
|
||||
THING_TYPE_BUS_AUTOMATION);
|
||||
|
||||
// ## Thermoregulation
|
||||
public static final Set<ThingTypeUID> THERMOREGULATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_THERMO_ZONE,
|
||||
THING_TYPE_BUS_THERMO_SENSOR);
|
||||
|
||||
// ## Energy Management
|
||||
public static final Set<ThingTypeUID> ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_ENERGY_METER);
|
||||
|
||||
// ## CEN/CEN+ Scenario
|
||||
public static final Set<ThingTypeUID> SCENARIO_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_CEN_SCENARIO_CONTROL,
|
||||
THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL);
|
||||
// ## Groups
|
||||
public static final Set<ThingTypeUID> DEVICE_SUPPORTED_THING_TYPES = Stream
|
||||
.of(LIGHTING_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES,
|
||||
THERMOREGULATION_SUPPORTED_THING_TYPES, ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES,
|
||||
GENERIC_SUPPORTED_THING_TYPES)
|
||||
SCENARIO_SUPPORTED_THING_TYPES, GENERIC_SUPPORTED_THING_TYPES)
|
||||
.flatMap(Collection::stream).collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
public static final Set<ThingTypeUID> BRIDGE_SUPPORTED_THING_TYPES = Set.of(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));
|
||||
@@ -109,10 +113,8 @@ public class OpenWebNetBindingConstants {
|
||||
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";
|
||||
|
||||
// thermo
|
||||
public static final String CHANNEL_TEMPERATURE = "temperature";
|
||||
public static final String CHANNEL_FUNCTION = "function";
|
||||
@@ -122,18 +124,20 @@ public class OpenWebNetBindingConstants {
|
||||
public static final String CHANNEL_CONDITIONING_VALVES = "conditioningValves";
|
||||
public static final String CHANNEL_HEATING_VALVES = "heatingValves";
|
||||
public static final String CHANNEL_ACTUATORS = "actuators";
|
||||
|
||||
// energy management
|
||||
public static final String CHANNEL_POWER = "power";
|
||||
// scenario button channels
|
||||
public static final String CHANNEL_SCENARIO_BUTTON = "button#";
|
||||
public static final String CHANNEL_TYPE_CEN_BUTTON_EVENT = "cenButtonEvent";
|
||||
public static final String CHANNEL_TYPE_CEN_PLUS_BUTTON_EVENT = "cenPlusButtonEvent";
|
||||
|
||||
// devices config properties
|
||||
public static final String CONFIG_PROPERTY_WHERE = "where";
|
||||
public static final String CONFIG_PROPERTY_SHUTTER_RUN = "shutterRun";
|
||||
|
||||
public static final String CONFIG_PROPERTY_SCENARIO_BUTTONS = "buttons";
|
||||
// BUS gw config properties
|
||||
public static final String CONFIG_PROPERTY_HOST = "host";
|
||||
public static final String CONFIG_PROPERTY_SERIAL_PORT = "serialPort";
|
||||
|
||||
// properties
|
||||
public static final String PROPERTY_OWNID = "ownId";
|
||||
public static final String PROPERTY_ZIGBEEID = "zigbeeid";
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.openhab.binding.openwebnet.internal.handler.OpenWebNetBridgeHandler;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetEnergyHandler;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetGenericHandler;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetLightingHandler;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetThermoregulationHandler;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
@@ -70,6 +71,9 @@ public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
|
||||
} else if (OpenWebNetThermoregulationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW THERMO Handler");
|
||||
return new OpenWebNetThermoregulationHandler(thing);
|
||||
} else if (OpenWebNetScenarioHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
logger.debug("creating NEW SCENARIO Handler");
|
||||
return new OpenWebNetScenarioHandler(thing);
|
||||
}
|
||||
logger.warn("ThingType {} is not supported by this binding", thing.getThingTypeUID());
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.actions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.ActionOutput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.communication.Response;
|
||||
import org.openwebnet4j.message.CEN;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetCENActions} defines CEN/CEN+ actions for the openwebnet binding.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
|
||||
@ThingActionsScope(name = "openwebnet")
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetCENActions implements ThingActions {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetCENActions.class);
|
||||
private @Nullable OpenWebNetScenarioHandler scenarioHandler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
this.scenarioHandler = (OpenWebNetScenarioHandler) handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return scenarioHandler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "virtualPress", description = "Virtual press of the push button")
|
||||
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean virtualPress(
|
||||
@ActionInput(name = "press", label = "press", description = "Type of press") @Nullable String press,
|
||||
@ActionInput(name = "button", label = "button", description = "Button number") int button) {
|
||||
OpenWebNetScenarioHandler handler = scenarioHandler;
|
||||
if (handler == null) {
|
||||
logger.warn("openwebnet OpenWebNetCENActions: scenarioHandler is null!");
|
||||
return false;
|
||||
}
|
||||
if (press == null) {
|
||||
logger.warn("openwebnet OpenWebNetCENActions: press parameter is null!");
|
||||
return false;
|
||||
}
|
||||
CEN msg = null;
|
||||
try {
|
||||
msg = handler.pressStrToMessage(press, button);
|
||||
Response res = handler.send(msg);
|
||||
if (res != null) {
|
||||
logger.debug("Sent virtualPress '{}' to gateway. Response: {}", msg, res.getResponseMessages());
|
||||
return res.isSuccess();
|
||||
} else {
|
||||
logger.debug("virtual press action returned null response");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("cannot execute virtual press action for thing {}: {}", handler.getThing().getUID(),
|
||||
e.getMessage());
|
||||
} catch (OWNException e) {
|
||||
logger.warn("exception while sending virtual press message '{}' to gateway: {}", msg, e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// legacy delegate methods
|
||||
public static void virtualPress(@Nullable ThingActions actions, @Nullable String press, int button) {
|
||||
if (actions instanceof OpenWebNetCENActions) {
|
||||
((OpenWebNetCENActions) actions).virtualPress(press, button);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Instance is not an OpenWebNetCENActions class.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,18 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
|
||||
deviceWho = Who.ENERGY_MANAGEMENT;
|
||||
break;
|
||||
}
|
||||
case SCENARIO_CONTROL: {
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CEN_SCENARIO_CONTROL;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CEN_SCENARIO_CONTROL;
|
||||
deviceWho = Who.CEN_SCENARIO_SCHEDULER;
|
||||
break;
|
||||
}
|
||||
case MULTIFUNCTION_SCENARIO_CONTROL: {
|
||||
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL;
|
||||
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL;
|
||||
deviceWho = Who.CEN_PLUS_SCENARIO_SCHEDULER;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logger.warn("Device type {} is not supported, default to GENERIC device (WHERE={})", deviceType, where);
|
||||
if (where instanceof WhereZigBee) {
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.openwebnet4j.communication.OWNAuthException;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.message.Automation;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.CEN;
|
||||
import org.openwebnet4j.message.EnergyManagement;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.GatewayMgmt;
|
||||
@@ -308,7 +309,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
}
|
||||
// we support these types only
|
||||
if (baseMsg instanceof Lighting || baseMsg instanceof Automation || baseMsg instanceof EnergyManagement
|
||||
|| baseMsg instanceof Thermoregulation) {
|
||||
|| baseMsg instanceof Thermoregulation || baseMsg instanceof CEN) {
|
||||
BaseOpenMessage bmsg = baseMsg;
|
||||
if (baseMsg instanceof Lighting) {
|
||||
What what = baseMsg.getWhat();
|
||||
@@ -419,7 +420,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
BaseOpenMessage baseMsg = (BaseOpenMessage) msg;
|
||||
// let's try to get the Thing associated with this message...
|
||||
if (baseMsg instanceof Lighting || baseMsg instanceof Automation || baseMsg instanceof EnergyManagement
|
||||
|| baseMsg instanceof Thermoregulation) {
|
||||
|| baseMsg instanceof Thermoregulation || baseMsg instanceof CEN) {
|
||||
String ownId = ownIdFromMessage(baseMsg);
|
||||
logger.debug("ownIdFromMessage({}) --> {}", baseMsg, ownId);
|
||||
OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
|
||||
|
||||
@@ -107,20 +107,23 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
|
||||
"subscribeToActivePowerChanges() Refreshing subscription for the next {}min for WHERE={} to active power changes notification",
|
||||
ENERGY_SUBSCRIPTION_PERIOD, deviceWhere);
|
||||
}
|
||||
|
||||
try {
|
||||
bridgeHandler.gateway.send(EnergyManagement.setActivePowerNotificationsTime(deviceWhere.value(),
|
||||
ENERGY_SUBSCRIPTION_PERIOD));
|
||||
isFirstSchedulerLaunch = false;
|
||||
} catch (Exception e) {
|
||||
if (isFirstSchedulerLaunch) {
|
||||
logger.warn(
|
||||
"subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
|
||||
deviceWhere, e.getMessage());
|
||||
} else {
|
||||
logger.warn(
|
||||
"subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
|
||||
deviceWhere, e.getMessage());
|
||||
Where w = deviceWhere;
|
||||
if (w == null) {
|
||||
logger.warn("subscribeToActivePowerChanges() WHERE=null. Skipping");
|
||||
} else {
|
||||
try {
|
||||
send(EnergyManagement.setActivePowerNotificationsTime(w.value(), ENERGY_SUBSCRIPTION_PERIOD));
|
||||
isFirstSchedulerLaunch = false;
|
||||
} catch (Exception e) {
|
||||
if (isFirstSchedulerLaunch) {
|
||||
logger.warn(
|
||||
"subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
|
||||
w, e.getMessage());
|
||||
} else {
|
||||
logger.warn(
|
||||
"subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
|
||||
w, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, ENERGY_SUBSCRIPTION_PERIOD - 1, TimeUnit.MINUTES);
|
||||
@@ -129,9 +132,9 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (notificationSchedule != null) {
|
||||
ScheduledFuture<?> ns = notificationSchedule;
|
||||
ns.cancel(false);
|
||||
logger.debug("dispose() scheduler stopped.");
|
||||
|
||||
notificationSchedule.cancel(false);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.handler;
|
||||
|
||||
import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
|
||||
import org.openhab.binding.openwebnet.internal.actions.OpenWebNetCENActions;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.CEN;
|
||||
import org.openwebnet4j.message.CEN.Pressure;
|
||||
import org.openwebnet4j.message.CENPlusScenario;
|
||||
import org.openwebnet4j.message.CENPlusScenario.CENPlusPressure;
|
||||
import org.openwebnet4j.message.CENScenario;
|
||||
import org.openwebnet4j.message.CENScenario.CENPressure;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereCEN;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenWebNetScenarioHandler} is responsible for handling commands/messages for CEN/CEN+ Scenarios. It
|
||||
* extends the abstract {@link OpenWebNetThingHandler}.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenWebNetScenarioHandler extends OpenWebNetThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenWebNetScenarioHandler.class);
|
||||
|
||||
private interface PressEvent {
|
||||
@Override
|
||||
public String toString();
|
||||
}
|
||||
|
||||
private enum CENPressEvent implements PressEvent {
|
||||
CEN_EVENT_START_PRESS("START_PRESS"),
|
||||
CEN_EVENT_SHORT_PRESS("SHORT_PRESS"),
|
||||
CEN_EVENT_EXTENDED_PRESS("EXTENDED_PRESS"),
|
||||
CEN_EVENT_RELEASE_EXTENDED_PRESS("RELEASE_EXTENDED_PRESS");
|
||||
|
||||
private final String press;
|
||||
|
||||
CENPressEvent(final String pr) {
|
||||
this.press = pr;
|
||||
}
|
||||
|
||||
public static @Nullable CENPressEvent fromValue(String s) {
|
||||
Optional<CENPressEvent> event = Arrays.stream(values()).filter(val -> s.equals(val.press)).findFirst();
|
||||
return event.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return press;
|
||||
}
|
||||
}
|
||||
|
||||
private enum CENPlusPressEvent implements PressEvent {
|
||||
CENPLUS_EVENT_SHORT_PRESS("SHORT_PRESS"),
|
||||
CENPLUS_EVENT_START_EXTENDED_PRESS("START_EXTENDED_PRESS"),
|
||||
CENPLUS_EVENT_EXTENDED_PRESS("EXTENDED_PRESS"),
|
||||
CENPLUS_EVENT_RELEASE_EXTENDED_PRESS("RELEASE_EXTENDED_PRESS");
|
||||
|
||||
private final String press;
|
||||
|
||||
CENPlusPressEvent(final String pr) {
|
||||
this.press = pr;
|
||||
}
|
||||
|
||||
public static @Nullable CENPlusPressEvent fromValue(String s) {
|
||||
Optional<CENPlusPressEvent> event = Arrays.stream(values()).filter(val -> s.equals(val.press)).findFirst();
|
||||
return event.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return press;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCENPlus = false;
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.SCENARIO_SUPPORTED_THING_TYPES;
|
||||
|
||||
public OpenWebNetScenarioHandler(Thing thing) {
|
||||
super(thing);
|
||||
if (OpenWebNetBindingConstants.THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL.equals(thing.getThingTypeUID())) {
|
||||
isCENPlus = true;
|
||||
logger.debug("created CEN+ device for thing: {}", getThing().getUID());
|
||||
} else {
|
||||
logger.debug("created CEN device for thing: {}", getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
Object buttonsConfig = getConfig().get(CONFIG_PROPERTY_SCENARIO_BUTTONS);
|
||||
if (buttonsConfig != null) {
|
||||
Set<Integer> buttons = csvStringToSetInt((String) buttonsConfig);
|
||||
if (!buttons.isEmpty()) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
Channel ch;
|
||||
for (Integer i : buttons) {
|
||||
ch = thing.getChannel(CHANNEL_SCENARIO_BUTTON + i);
|
||||
if (ch == null) {
|
||||
thingBuilder.withChannel(buttonToChannel(i));
|
||||
logger.debug("added channel {} to thing: {}", i, getThing().getUID());
|
||||
}
|
||||
}
|
||||
updateThing(thingBuilder.build());
|
||||
} else {
|
||||
logger.warn("invalid config parameter buttons='{}' for thing {}", buttonsConfig, thing.getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(OpenWebNetCENActions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String ownIdPrefix() {
|
||||
if (isCENPlus) {
|
||||
return Who.CEN_PLUS_SCENARIO_SCHEDULER.value().toString();
|
||||
} else {
|
||||
return Who.CEN_SCENARIO_SCHEDULER.value().toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.isCommand()) {
|
||||
triggerChannel((CEN) msg);
|
||||
} else {
|
||||
logger.debug("handleMessage() Ignoring unsupported DIM for thing {}. Frame={}", getThing().getUID(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerChannel(CEN cenMsg) {
|
||||
Integer buttonNumber;
|
||||
try {
|
||||
buttonNumber = cenMsg.getButtonNumber();
|
||||
} catch (FrameException e) {
|
||||
logger.warn("cannot read CEN/CEN+ button. Ignoring message {}", cenMsg);
|
||||
return;
|
||||
}
|
||||
if (buttonNumber == null || buttonNumber < 0 || buttonNumber > 31) {
|
||||
logger.warn("invalid CEN/CEN+ button number: {}. Ignoring message {}", buttonNumber, cenMsg);
|
||||
return;
|
||||
}
|
||||
Channel ch = thing.getChannel(CHANNEL_SCENARIO_BUTTON + buttonNumber);
|
||||
if (ch == null) { // we have found a new button for this device, let's add a new channel for the button
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
ch = buttonToChannel(buttonNumber);
|
||||
thingBuilder.withChannel(ch);
|
||||
updateThing(thingBuilder.build());
|
||||
logger.info("added new channel {} to thing {}", ch.getUID(), getThing().getUID());
|
||||
}
|
||||
final Channel channel = ch;
|
||||
PressEvent pressEv = null;
|
||||
Pressure press = null;
|
||||
try {
|
||||
press = cenMsg.getButtonPressure();
|
||||
} catch (FrameException e) {
|
||||
logger.warn("invalid CEN/CEN+ Press. Ignoring message {}", cenMsg);
|
||||
return;
|
||||
}
|
||||
if (press == null) {
|
||||
logger.warn("invalid CEN/CEN+ Press. Ignoring message {}", cenMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cenMsg instanceof CENScenario) {
|
||||
switch ((CENPressure) press) {
|
||||
case START_PRESSURE:
|
||||
pressEv = CENPressEvent.CEN_EVENT_START_PRESS;
|
||||
break;
|
||||
case RELEASE_SHORT_PRESSURE:
|
||||
pressEv = CENPressEvent.CEN_EVENT_SHORT_PRESS;
|
||||
break;
|
||||
case EXTENDED_PRESSURE:
|
||||
pressEv = CENPressEvent.CEN_EVENT_EXTENDED_PRESS;
|
||||
break;
|
||||
case RELEASE_EXTENDED_PRESSURE:
|
||||
pressEv = CENPressEvent.CEN_EVENT_RELEASE_EXTENDED_PRESS;
|
||||
break;
|
||||
default:
|
||||
logger.warn("unsupported CENPress. Ignoring message {}", cenMsg);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch ((CENPlusPressure) press) {
|
||||
case SHORT_PRESSURE:
|
||||
pressEv = CENPlusPressEvent.CENPLUS_EVENT_SHORT_PRESS;
|
||||
break;
|
||||
case START_EXTENDED_PRESSURE:
|
||||
pressEv = CENPlusPressEvent.CENPLUS_EVENT_START_EXTENDED_PRESS;
|
||||
break;
|
||||
case EXTENDED_PRESSURE:
|
||||
pressEv = CENPlusPressEvent.CENPLUS_EVENT_EXTENDED_PRESS;
|
||||
break;
|
||||
case RELEASE_EXTENDED_PRESSURE:
|
||||
pressEv = CENPlusPressEvent.CENPLUS_EVENT_RELEASE_EXTENDED_PRESS;
|
||||
break;
|
||||
default:
|
||||
logger.warn("unsupported CENPlusPress. Ignoring message {}", cenMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
triggerChannel(channel.getUID(), pressEv.toString());
|
||||
}
|
||||
|
||||
private Channel buttonToChannel(int buttonNumber) {
|
||||
ChannelTypeUID channelTypeUID;
|
||||
if (isCENPlus) {
|
||||
channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_CEN_PLUS_BUTTON_EVENT);
|
||||
} else {
|
||||
channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_CEN_BUTTON_EVENT);
|
||||
}
|
||||
return ChannelBuilder
|
||||
.create(new ChannelUID(getThing().getUID(), CHANNEL_SCENARIO_BUTTON + buttonNumber), "String")
|
||||
.withType(channelTypeUID).withKind(ChannelKind.TRIGGER).withLabel("Button " + buttonNumber).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a CEN/CEN+ virtual press message for this device given a pressString and button number
|
||||
*
|
||||
* @param pressString one START_PRESS, SHORT_PRESS etc.
|
||||
* @param button number [0-31]
|
||||
* @return CEN message
|
||||
* @throws IllegalArgumentException if button number or pressString are invalid
|
||||
*/
|
||||
public CEN pressStrToMessage(String pressString, int button) throws IllegalArgumentException {
|
||||
Where w = deviceWhere;
|
||||
if (w == null) {
|
||||
throw new IllegalArgumentException("pressStrToMessage: deviceWhere is null");
|
||||
}
|
||||
if (isCENPlus) {
|
||||
CENPlusPressEvent prEvent = CENPlusPressEvent.fromValue(pressString);
|
||||
if (prEvent != null) {
|
||||
switch (prEvent) {
|
||||
case CENPLUS_EVENT_SHORT_PRESS:
|
||||
return CENPlusScenario.virtualShortPressure(w.value(), button);
|
||||
case CENPLUS_EVENT_START_EXTENDED_PRESS:
|
||||
return CENPlusScenario.virtualStartExtendedPressure(w.value(), button);
|
||||
case CENPLUS_EVENT_EXTENDED_PRESS:
|
||||
return CENPlusScenario.virtualExtendedPressure(w.value(), button);
|
||||
case CENPLUS_EVENT_RELEASE_EXTENDED_PRESS:
|
||||
return CENPlusScenario.virtualReleaseExtendedPressure(w.value(), button);
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported press type: " + pressString);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported press type: " + pressString);
|
||||
}
|
||||
} else {
|
||||
CENPressEvent prEvent = CENPressEvent.fromValue(pressString);
|
||||
if (prEvent != null) {
|
||||
switch (prEvent) {
|
||||
case CEN_EVENT_START_PRESS:
|
||||
return CENScenario.virtualStartPressure(w.value(), button);
|
||||
case CEN_EVENT_SHORT_PRESS:
|
||||
return CENScenario.virtualReleaseShortPressure(w.value(), button);
|
||||
case CEN_EVENT_EXTENDED_PRESS:
|
||||
return CENScenario.virtualExtendedPressure(w.value(), button);
|
||||
case CEN_EVENT_RELEASE_EXTENDED_PRESS:
|
||||
return CENScenario.virtualReleaseExtendedPressure(w.value(), button);
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported press type: " + pressString);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported press type: " + pressString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Integer> csvStringToSetInt(String s) {
|
||||
TreeSet<Integer> intSet = new TreeSet<Integer>();
|
||||
String sNorm = s.replaceAll("\\s", "");
|
||||
Scanner sc = new Scanner(sNorm);
|
||||
sc.useDelimiter(",");
|
||||
while (sc.hasNextInt()) {
|
||||
intSet.add(sc.nextInt());
|
||||
}
|
||||
sc.close();
|
||||
return intSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleChannelCommand(ChannelUID channel, Command command) {
|
||||
logger.warn("CEN/CEN+ channels are trigger channels and do not handle commands");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshDevice(boolean refreshAll) {
|
||||
logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
|
||||
return new WhereCEN(wStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestChannelState(ChannelUID channel) {
|
||||
logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
|
||||
/**
|
||||
* Helper method to send OWN messages from ThingHandlers
|
||||
*/
|
||||
protected @Nullable Response send(OpenMessage msg) throws OWNException {
|
||||
public @Nullable Response send(OpenMessage msg) throws OWNException {
|
||||
OpenWebNetBridgeHandler bh = bridgeHandler;
|
||||
if (bh != null) {
|
||||
OpenGateway gw = bh.gateway;
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</parameter>
|
||||
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet 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>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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 CEN+ Scenario Control (BTicino HC/HD/HS/L/N/NT4680) -->
|
||||
<thing-type id="bus_cenplus_scenario_control">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>CEN+ Scenario Control</label>
|
||||
<description>A OpenWebNet BUS/SCS CEN+ Scenario Control device. BTicino models: HC/HD/HS/L/N/NT4680</description>
|
||||
|
||||
<!-- channels are created dynamically based on configured buttons -->
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-HC/HD/HS/L/N/NT4680</property>
|
||||
<property name="ownDeviceType">273</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="buttons" type="text">
|
||||
<label>Configured Buttons</label>
|
||||
<description>List (comma separated) of buttons numbers [0-31] configured for this scenario device, example:
|
||||
buttons=1,2,4
|
||||
</description>
|
||||
</parameter>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>Use 2+N[0-2047]. Example: scenario control 5 --> WHERE=25</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">
|
||||
|
||||
<!-- Thing for BUS CEN Scenario Control (BTicino HC/HD/HS/L/N/NT4680) -->
|
||||
<thing-type id="bus_cen_scenario_control">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bus_gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>CEN Scenario Control</label>
|
||||
<description>A OpenWebNet BUS/SCS CEN Scenario Control device. BTicino models: HC/HD/HS/L/N/NT4680</description>
|
||||
|
||||
<!-- channels are created dynamically based on configured buttons -->
|
||||
|
||||
<properties>
|
||||
<property name="vendor">BTicino/Legrand</property>
|
||||
<property name="model">BTI-HC/HD/HS/L/N/NT4680</property>
|
||||
<property name="ownDeviceType">2</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ownId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="buttons" type="text">
|
||||
<label>Configured Buttons</label>
|
||||
<description>List (comma separated) of buttons numbers [0-31] configured for this scenario device. Example:
|
||||
buttons=1,2,4</description>
|
||||
</parameter>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet 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>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet 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>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Address</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>Example: 5N with N=[1-255]</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet 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>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>Example: sensor 3 of zone 2 --> where=302. Sensor 5 of external zone 00 --> where=500</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>Example: zone 2 --> where=2.</description>
|
||||
</parameter>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<default>AUTO</default>
|
||||
</parameter>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>It identifies one ZigBee device. Example: 765432101#9</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>It identifies one ZigBee device. Example: 765432101#9</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>It identifies one ZigBee device. Example: 765432101#9</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<config-description>
|
||||
<parameter name="where" type="text" required="true">
|
||||
<label>OpenWebNet Device Address (where)</label>
|
||||
<label>OpenWebNet Address (where)</label>
|
||||
<description>It identifies one ZigBee device. Example: 765432100#9 (use unit=00 at the end)</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@@ -178,4 +178,31 @@
|
||||
<category>Energy</category>
|
||||
<state readOnly="true" pattern="%.0f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<!-- CEN/CEN+ trigger channels -->
|
||||
<channel-type id="cenButtonEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>CEN Button Event</label>
|
||||
<event>
|
||||
<options>
|
||||
<option value="START_PRESS">start press</option>
|
||||
<option value="SHORT_PRESS">release after short press</option>
|
||||
<option value="EXTENDED_PRESS">extended press (repeated until release)</option>
|
||||
<option value="RELEASE_EXTENDED_PRESS">release after extended press</option>
|
||||
</options>
|
||||
</event>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="cenPlusButtonEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>CEN+ Button Event</label>
|
||||
<event>
|
||||
<options>
|
||||
<option value="SHORT_PRESS">short press</option>
|
||||
<option value="START_EXTENDED_PRESS">start of extended press</option>
|
||||
<option value="EXTENDED_PRESS">extended press (repeated until release)</option>
|
||||
<option value="RELEASE_EXTENDED_PRESS">release after extended press</option>
|
||||
</options>
|
||||
</event>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
|
||||
Reference in New Issue
Block a user