[openwebnet] Add support for Alarm (WHO=5) (#13694)

* [openwebnet] first changes for Alarm support
* [openwebnet] added first version Alarm handler
* [openwebnet] added ownIdTest for Alarm
* [openwebnet] added things def for Alarm and discovery for alarm zone
* [openwebnet] refreshDevice
* [openwebnet] fix text formatting
* [openwebnet] fixes
* [openwebnet] added other alarm channels
* [openwebnet] Alarm: codestyle and null checks
* [openwebnet] updated Alarm examples in README and some cleanup
* [openwebnet] changed zone channel from armed to state, cleaned README

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
This commit is contained in:
M Valla
2022-12-05 12:57:25 +01:00
committed by GitHub
parent 590b2cb4f7
commit 7ce99b365a
12 changed files with 626 additions and 167 deletions

View File

@@ -22,7 +22,8 @@ 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.
* The {@link OpenWebNetBindingConstants} class defines common constants, which
* are used across the whole binding.
*
* @author Massimo Valla - Initial contribution, updates
* @author Gilberto Cocchi - Thermoregulation
@@ -72,6 +73,10 @@ public class OpenWebNetBindingConstants {
public static final String THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL = "CEN+ Scenario Control";
public static final ThingTypeUID THING_TYPE_BUS_SCENARIO = new ThingTypeUID(BINDING_ID, "bus_scenario_control");
public static final String THING_LABEL_BUS_SCENARIO = "Scenario Control";
public static final ThingTypeUID THING_TYPE_BUS_ALARM_SYSTEM = new ThingTypeUID(BINDING_ID, "bus_alarm_system");
public static final String THING_LABEL_BUS_ALARM_SYSTEM = "Alarm System";
public static final ThingTypeUID THING_TYPE_BUS_ALARM_ZONE = new ThingTypeUID(BINDING_ID, "bus_alarm_zone");
public static final String THING_LABEL_BUS_ALARM_ZONE = "Alarm Zone";
public static final ThingTypeUID THING_TYPE_BUS_AUX = new ThingTypeUID(BINDING_ID, "bus_aux");
public static final String THING_LABEL_BUS_AUX = "Auxiliary";
// ZIGBEE
@@ -107,12 +112,16 @@ public class OpenWebNetBindingConstants {
public static final Set<ThingTypeUID> SCENARIO_BASIC_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_SCENARIO);
// ## Aux
public static final Set<ThingTypeUID> AUX_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_AUX);
// ## Alarm
public static final Set<ThingTypeUID> ALARM_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_ALARM_SYSTEM,
THING_TYPE_BUS_ALARM_ZONE);
// ## 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,
SCENARIO_SUPPORTED_THING_TYPES, SCENARIO_BASIC_SUPPORTED_THING_TYPES, AUX_SUPPORTED_THING_TYPES,
GENERIC_SUPPORTED_THING_TYPES)
ALARM_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);
@@ -157,6 +166,13 @@ public class OpenWebNetBindingConstants {
public static final String CHANNEL_SCENARIO = "scenario";
// aux
public static final String CHANNEL_AUX = "aux";
// alarm
public static final String CHANNEL_ALARM_SYSTEM_STATE = "state";
public static final String CHANNEL_ALARM_SYSTEM_ARMED = "armed";
public static final String CHANNEL_ALARM_SYSTEM_NETWORK = "network";
public static final String CHANNEL_ALARM_SYSTEM_BATTERY = "battery";
public static final String CHANNEL_ALARM_ZONE_STATE = "state";
public static final String CHANNEL_ALARM_ZONE_ALARM_STATE = "alarm";
// devices config properties
public static final String CONFIG_PROPERTY_WHERE = "where";

View File

@@ -16,6 +16,7 @@ import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetAlarmHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetAutomationHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetAuxiliaryHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetBridgeHandler;
@@ -83,6 +84,9 @@ public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
} else if (OpenWebNetScenarioBasicHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("Creating NEW BASIC SCENARIO Handler");
return new OpenWebNetScenarioBasicHandler(thing);
} else if (OpenWebNetAlarmHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW ALARM Handler");
return new OpenWebNetAlarmHandler(thing);
}
logger.warn("ThingType {} is not supported by this binding", thing.getThingTypeUID());
return null;

View File

@@ -31,6 +31,7 @@ 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.WhereAlarm;
import org.openwebnet4j.message.WhereThermo;
import org.openwebnet4j.message.WhereZigBee;
import org.openwebnet4j.message.Who;
@@ -95,7 +96,8 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
* @param message the OWN message received that identified the device
* (optional)
*/
public void newDiscoveryResult(Where where, OpenDeviceType deviceType, @Nullable BaseOpenMessage baseMsg) {
public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,
@Nullable BaseOpenMessage baseMsg) {
logger.debug("newDiscoveryResult() WHERE={}, deviceType={}", where, deviceType);
ThingTypeUID thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE; // generic device
String thingLabel = OpenWebNetBindingConstants.THING_LABEL_GENERIC_DEVICE;
@@ -189,6 +191,18 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
deviceWho = Who.AUX;
break;
}
case SCS_ALARM_CENTRAL_UNIT: {
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_SYSTEM;
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ALARM_SYSTEM;
deviceWho = Who.BURGLAR_ALARM;
break;
}
case SCS_ALARM_ZONE: {
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_ZONE;
thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ALARM_ZONE;
deviceWho = Who.BURGLAR_ALARM;
break;
}
default:
logger.warn("Device type {} is not supported, default to GENERIC device (WHERE={})", deviceType, where);
if (where instanceof WhereZigBee) {
@@ -198,35 +212,46 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
deviceWho = baseMsg.getWho();
}
}
Where w;
if (where != null) {
w = where;
} else if (OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_SYSTEM.equals(thingTypeUID)) {
w = new WhereAlarm("0");
} else {
logger.debug("ignoring newDiscoveryResult with null where: {}", baseMsg);
return;
}
String ownId = bridgeHandler.ownIdFromWhoWhere(deviceWho, where);
String ownId = bridgeHandler.ownIdFromWhoWhere(deviceWho, w);
if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)) {
if (bridgeHandler.getRegisteredDevice(ownId) != null) {
logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", where);
logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", w);
return;
}
}
String tId = bridgeHandler.thingIdFromWhere(where);
String tId = bridgeHandler.thingIdFromWhere(w);
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
DiscoveryResult discoveryResult = null;
String whereConfig = where.value();
String whereConfig = w.value();
// remove # from discovered thermo zone or central unit
// remove # from discovered thermo zone/central unit or alarm zone
if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE.equals(thingTypeUID)
|| OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingTypeUID)) {
whereConfig = "" + ((WhereThermo) where).getZone();
} else if (OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_ZONE.equals(thingTypeUID)) {
whereConfig = "" + ((WhereAlarm) where).getZone();
}
if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) {
if (w instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) {
logger.debug("UNIT=02 found (WHERE={}) -> will remove previous result if exists", where);
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);
whereConfig = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // replace unit '02' with '00'
whereConfig = ((WhereZigBee) w).valueWithUnit(WhereZigBee.UNIT_ALL); // replace unit '02' with '00'
logger.debug("UNIT=02, switching type from {} to {}",
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH,
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS);

View File

@@ -0,0 +1,233 @@
/**
* Copyright (c) 2010-2022 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.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
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.openwebnet4j.communication.OWNException;
import org.openwebnet4j.message.Alarm;
import org.openwebnet4j.message.Alarm.WhatAlarm;
import org.openwebnet4j.message.BaseOpenMessage;
import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereAlarm;
import org.openwebnet4j.message.Who;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OpenWebNetAlarmHandler} is responsible for handling
* commands/messages for Alarm system and zones. It extends the abstract
* {@link OpenWebNetThingHandler}.
*
* @author Massimo Valla - Initial contribution
*/
@NonNullByDefault
public class OpenWebNetAlarmHandler extends OpenWebNetThingHandler {
private final Logger logger = LoggerFactory.getLogger(OpenWebNetAlarmHandler.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.ALARM_SUPPORTED_THING_TYPES;
private static long lastAllDevicesRefreshTS = 0; // ts when last all device refresh was sent for this handler
private static final String BATTERY_OK = "OK";
private static final String BATTERY_FAULT = "FAULT";
private static final String BATTERY_UNLOADED = "UNLOADED";
private static final String SILENT = "SILENT";
private static final String INTRUSION = "INTRUSION";
private static final String ANTI_PANIC = "ANTI_PANIC";
private static final String TAMPERING = "TAMPERING";
public OpenWebNetAlarmHandler(Thing thing) {
super(thing);
}
@Override
protected void handleChannelCommand(ChannelUID channel, Command command) {
logger.warn("Alarm.handleChannelCommand() Read only channel, unsupported command {}", command);
}
@Override
protected void requestChannelState(ChannelUID channel) {
super.requestChannelState(channel);
Where w = deviceWhere;
ThingTypeUID thingType = thing.getThingTypeUID();
try {
if (THING_TYPE_BUS_ALARM_SYSTEM.equals(thingType)) {
send(Alarm.requestSystemStatus());
lastAllDevicesRefreshTS = System.currentTimeMillis();
} else {
if (w != null) {
send(Alarm.requestZoneStatus("#" + w.value()));
} else {
logger.debug("null where while requesting state for channel {}", channel);
}
}
} catch (OWNException e) {
logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
@Override
protected long getRefreshAllLastTS() {
return lastAllDevicesRefreshTS;
};
@Override
protected void refreshDevice(boolean refreshAll) {
if (refreshAll) {
logger.debug("--- refreshDevice() : refreshing all via ALARM CENTRAL UNIT... ({})", thing.getUID());
try {
send(Alarm.requestSystemStatus());
lastAllDevicesRefreshTS = System.currentTimeMillis();
} catch (OWNException e) {
logger.warn("Excpetion while requesting alarm system status: {}", e.getMessage());
}
} else {
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_ALARM_SYSTEM_STATE));
}
}
@Override
protected void handleMessage(BaseOpenMessage msg) {
logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
super.handleMessage(msg);
ThingTypeUID thingType = thing.getThingTypeUID();
if (THING_TYPE_BUS_ALARM_SYSTEM.equals(thingType)) {
updateSystem((Alarm) msg);
} else {
updateZone((Alarm) msg);
}
}
private void updateSystem(Alarm msg) {
WhatAlarm w = (WhatAlarm) msg.getWhat();
if (w == null) {
logger.debug("Alarm.updateSystem() WHAT is null. Frame={}", msg);
return;
}
switch (w) {
case SYSTEM_ACTIVE:
case SYSTEM_INACTIVE:
case SYSTEM_MAINTENANCE:
updateAlarmSystemState(w);
break;
case SYSTEM_DISENGAGED:
case SYSTEM_ENGAGED:
updateAlarmSystemArmed(w);
break;
case SYSTEM_BATTERY_FAULT:
case SYSTEM_BATTERY_OK:
case SYSTEM_BATTERY_UNLOADED:
updateBatteryState(w);
break;
case SYSTEM_NETWORK_ERROR:
case SYSTEM_NETWORK_OK:
updateNetworkState(w);
break;
case START_PROGRAMMING:
case STOP_PROGRAMMING:
case DELAY_END:
case NO_CONNECTION_TO_DEVICE:
default:
logger.debug("Alarm.updateSystem() Ignoring unsupported WHAT {}. Frame={}", msg.getWhat(), msg);
}
}
private void updateAlarmSystemState(WhatAlarm w) {
updateState(CHANNEL_ALARM_SYSTEM_STATE, OnOffType.from(w == Alarm.WhatAlarm.SYSTEM_ACTIVE));
}
private void updateAlarmSystemArmed(WhatAlarm w) {
updateState(CHANNEL_ALARM_SYSTEM_ARMED, OnOffType.from(w == Alarm.WhatAlarm.SYSTEM_ENGAGED));
}
private void updateNetworkState(WhatAlarm w) {
updateState(CHANNEL_ALARM_SYSTEM_NETWORK, OnOffType.from(w == Alarm.WhatAlarm.SYSTEM_NETWORK_OK));
}
private void updateBatteryState(WhatAlarm w) {
if (w == Alarm.WhatAlarm.SYSTEM_BATTERY_OK) {
updateState(CHANNEL_ALARM_SYSTEM_BATTERY, new StringType(BATTERY_OK));
} else if (w == Alarm.WhatAlarm.SYSTEM_BATTERY_UNLOADED) {
updateState(CHANNEL_ALARM_SYSTEM_BATTERY, new StringType(BATTERY_UNLOADED));
} else {
updateState(CHANNEL_ALARM_SYSTEM_BATTERY, new StringType(BATTERY_FAULT));
}
}
private void updateZone(Alarm msg) {
WhatAlarm w = (WhatAlarm) msg.getWhat();
if (w == null) {
logger.debug("Alarm.updateZone() WHAT is null. Frame={}", msg);
return;
}
switch (w) {
case ZONE_DISENGAGED:
case ZONE_ENGAGED:
updateZoneState(w);
break;
case ZONE_ALARM_INTRUSION:
case ZONE_ALARM_TAMPERING:
case ZONE_ALARM_ANTI_PANIC:
case ZONE_ALARM_SILENT:
updateZoneAlarmState(w);
break;
case ZONE_ALARM_TECHNICAL:// not handled for now
case ZONE_ALARM_TECHNICAL_RESET:
default:
logger.debug("Alarm.updateZone() Ignoring unsupported WHAT {}. Frame={}", msg.getWhat(), msg);
}
}
private void updateZoneState(WhatAlarm w) {
updateState(CHANNEL_ALARM_ZONE_STATE, OnOffType.from(w == Alarm.WhatAlarm.ZONE_ENGAGED));
}
private void updateZoneAlarmState(WhatAlarm w) {
if (w == Alarm.WhatAlarm.ZONE_ALARM_SILENT) {
updateState(CHANNEL_ALARM_ZONE_ALARM_STATE, new StringType(SILENT));
} else if (w == Alarm.WhatAlarm.ZONE_ALARM_INTRUSION) {
updateState(CHANNEL_ALARM_ZONE_ALARM_STATE, new StringType(INTRUSION));
} else if (w == Alarm.WhatAlarm.ZONE_ALARM_ANTI_PANIC) {
updateState(CHANNEL_ALARM_ZONE_ALARM_STATE, new StringType(ANTI_PANIC));
} else {
updateState(CHANNEL_ALARM_ZONE_ALARM_STATE, new StringType(TAMPERING));
}
}
@Override
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
return new WhereAlarm(wStr);
}
@Override
protected String ownIdPrefix() {
return Who.BURGLAR_ALARM.value().toString();
}
}

View File

@@ -48,6 +48,7 @@ import org.openwebnet4j.OpenGateway;
import org.openwebnet4j.USBGateway;
import org.openwebnet4j.communication.OWNAuthException;
import org.openwebnet4j.communication.OWNException;
import org.openwebnet4j.message.Alarm;
import org.openwebnet4j.message.Automation;
import org.openwebnet4j.message.Auxiliary;
import org.openwebnet4j.message.BaseOpenMessage;
@@ -67,7 +68,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OpenWebNetBridgeHandler} is responsible for handling communication with gateways and handling events.
* The {@link OpenWebNetBridgeHandler} is responsible for handling communication
* with gateways and handling events.
*
* @author Massimo Valla - Initial contribution, Lighting, Automation, Scenario
* @author Andrea Conte - Energy management, Thermoregulation
@@ -95,7 +97,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
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
// association is: ownId (String) -> OpenWebNetThingHandler, with ownId =
// WHO.WHERE
private Map<String, @Nullable OpenWebNetThingHandler> registeredDevices = new ConcurrentHashMap<>();
private Map<String, Long> discoveringDevices = new ConcurrentHashMap<>();
@@ -296,10 +299,10 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
public void onNewDevice(@Nullable Where w, @Nullable OpenDeviceType deviceType, @Nullable BaseOpenMessage message) {
OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService;
if (discService != null) {
if (w != null && deviceType != null) {
if (deviceType != null) {
discService.newDiscoveryResult(w, deviceType, message);
} else {
logger.warn("onNewDevice with null where/deviceType, msg={}", message);
logger.warn("onNewDevice with null deviceType, msg={}", message);
}
} else {
logger.warn("onNewDevice but null deviceDiscoveryService");
@@ -312,7 +315,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
}
/**
* Notifies that the scan has been stopped/aborted by OpenWebNetDeviceDiscoveryService
* Notifies that the scan has been stopped/aborted by
* OpenWebNetDeviceDiscoveryService
*/
public void scanStopped() {
scanIsActive = false;
@@ -328,7 +332,8 @@ 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 CEN || baseMsg instanceof Scenario) {
|| baseMsg instanceof Thermoregulation || baseMsg instanceof CEN || baseMsg instanceof Scenario
|| baseMsg instanceof Alarm) {
BaseOpenMessage bmsg = baseMsg;
if (baseMsg instanceof Lighting) {
What what = baseMsg.getWhat();
@@ -433,7 +438,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
return;
}
} else if (System.currentTimeMillis() - lastRegisteredDeviceTS < REFRESH_ALL_DEVICES_DELAY_MSEC) {
// a device has been registered with the bridge just now, let's wait for other devices: re-schedule
// a device has been registered with the bridge just now, let's wait for other
// devices: re-schedule
// refreshAllDevices
logger.debug("--- REGISTER device just called... re-scheduling refreshAllBridgeDevices()");
refreshAllSchedule = scheduler.schedule(this::refreshAllBridgeDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
@@ -501,7 +507,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
// 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 CEN || baseMsg instanceof Auxiliary
|| baseMsg instanceof Scenario) {
|| baseMsg instanceof Scenario || baseMsg instanceof Alarm) {
String ownId = ownIdFromMessage(baseMsg);
logger.debug("ownIdFromMessage({}) --> {}", baseMsg, ownId);
OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
@@ -665,7 +671,16 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
* @return the ownId String
*/
public String ownIdFromMessage(BaseOpenMessage baseMsg) {
return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWhere());
@Nullable
Where w = baseMsg.getWhere();
if (w != null) {
return baseMsg.getWho().value() + "." + normalizeWhere(w);
} else if (baseMsg instanceof Alarm) { // null and Alarm
return baseMsg.getWho().value() + "." + "0"; // Alarm System --> where=0
} else {
logger.warn("ownIdFromMessage with null where: {}", baseMsg);
return "";
}
}
/**
@@ -690,7 +705,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
str = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // 76543210X#9 --> 765432100#9
} else {
if (str.indexOf("#4#") == -1) { // skip APL#4#bus case
if (str.indexOf('#') == 0) { // Thermo central unit (#0) or zone via central unit (#Z, Z=[1-99]) --> Z
if (str.indexOf('#') == 0) { // Thermo central unit (#0) or zone via central unit (#Z, Z=[1-99]) --> Z,
// Alarm Zone (#Z) --> Z
str = str.substring(1);
} else if (str.indexOf('#') > 0) { // Thermo zone Z and actuator N (Z#N, Z=[1-99], N=[1-9]) --> Z
str = str.substring(0, str.indexOf('#'));

View File

@@ -5,6 +5,10 @@ binding.openwebnet.description = The OpenWebNet Binding integrates the BTicino/L
# thing types
thing-type.openwebnet.bus_alarm_system.label = Alarm System
thing-type.openwebnet.bus_alarm_system.description = A OpenWebNet BUS/SCS alarm system.
thing-type.openwebnet.bus_alarm_zone.label = Alarm Zone
thing-type.openwebnet.bus_alarm_zone.description = A OpenWebNet BUS/SCS configured alarm zone.
thing-type.openwebnet.bus_automation.label = Automation
thing-type.openwebnet.bus_automation.description = An OpenWebNet BUS/SCS automation device to control roller shutters, blinds, etc. BTicino models: xxx/yyyy/etc.
thing-type.openwebnet.bus_aux.label = Auxiliary
@@ -46,6 +50,10 @@ thing-type.openwebnet.zb_on_off_switch2u.description = An OpenWebNet ZigBee 2-un
# thing types config
thing-type.config.openwebnet.bus_alarm_system.where.label = OpenWebNet Address (where)
thing-type.config.openwebnet.bus_alarm_system.where.description = The alarm system has no where address (set where to 0 as default).
thing-type.config.openwebnet.bus_alarm_zone.where.label = OpenWebNet Address (where)
thing-type.config.openwebnet.bus_alarm_zone.where.description = Example: alarm zone 2 --> where=2.
thing-type.config.openwebnet.bus_automation.shutterRun.label = Shutter Run
thing-type.config.openwebnet.bus_automation.shutterRun.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.
thing-type.config.openwebnet.bus_automation.where.label = OpenWebNet Address (where)
@@ -117,6 +125,19 @@ channel-type.openwebnet.actuators.state.option.ON_SPEED_3 = ON speed 3
channel-type.openwebnet.actuators.state.option.OFF_SPEED_1 = OFF speed 1
channel-type.openwebnet.actuators.state.option.OFF_SPEED_2 = OFF speed 2
channel-type.openwebnet.actuators.state.option.OFF_SPEED_3 = OFF speed 3
channel-type.openwebnet.alarmBattery.label = Battery State
channel-type.openwebnet.alarmBattery.description = Alarm system battery state (read only).
channel-type.openwebnet.alarmBattery.state.option.OK = Ok
channel-type.openwebnet.alarmBattery.state.option.FAULT = Fault
channel-type.openwebnet.alarmBattery.state.option.UNLOADED = Unloaded
channel-type.openwebnet.alarmNetwork.label = Network State
channel-type.openwebnet.alarmNetwork.description = Alarm system network state (ON = network OK, OFF = no network) (read only).
channel-type.openwebnet.alarmSystemState.label = System State
channel-type.openwebnet.alarmSystemState.description = Alarm system is active (ON) or inactive (OFF) (read only).
channel-type.openwebnet.alarmZoneState.label = Alarm Zone State
channel-type.openwebnet.alarmZoneState.description = Alarm zone is active (ON) or inactive (OFF) (read only).
channel-type.openwebnet.armedState.label = Armed State
channel-type.openwebnet.armedState.description = Alarm is armed (ON) or disarmed (OFF) (read only).
channel-type.openwebnet.atLeastOneProbeManual.label = At least one probe in MANUAL
channel-type.openwebnet.atLeastOneProbeManual.description = At least one probe in MANUAL indicator in Central Unit (read only)
channel-type.openwebnet.atLeastOneProbeOff.label = At least one probe OFF
@@ -144,20 +165,8 @@ channel-type.openwebnet.brightness.label = Brightness
channel-type.openwebnet.brightness.description = Control the brightness and switch the light ON and OFF (read/write)
channel-type.openwebnet.cenButtonEvent.label = CEN Button Event
channel-type.openwebnet.cenButtonEvent.description = Event corresponding to the button press (read only)
# channel-type.openwebnet.cenButtonEvent.event.option.START_PRESS = Start press
# channel-type.openwebnet.cenButtonEvent.event.option.SHORT_PRESS = Release after short press
# channel-type.openwebnet.cenButtonEvent.event.option.EXTENDED_PRESS = Extended press (repeated until release)
# channel-type.openwebnet.cenButtonEvent.event.option.RELEASE_EXTENDED_PRESS = Release after extended press
channel-type.openwebnet.cenPlusButtonEvent.label = CEN+ Button Event
channel-type.openwebnet.cenPlusButtonEvent.description = Event corresponding to the button press (read only)
# channel-type.openwebnet.cenPlusButtonEvent.event.option.SHORT_PRESS = Short press
# channel-type.openwebnet.cenPlusButtonEvent.event.option.START_EXTENDED_PRESS = Start of extended press
# channel-type.openwebnet.cenPlusButtonEvent.event.option.EXTENDED_PRESS = Extended press (repeated until release)
# channel-type.openwebnet.cenPlusButtonEvent.event.option.RELEASE_EXTENDED_PRESS = Release after extended press
channel-type.openwebnet.conditioningValves.label = Conditioning Valves
channel-type.openwebnet.conditioningValves.description = Conditioning Valves status (read only)
channel-type.openwebnet.conditioningValves.state.option.OFF = OFF
@@ -230,28 +239,6 @@ channel-type.openwebnet.remoteControl.state.option.DISABLED = DISABLED
channel-type.openwebnet.remoteControl.state.option.ENABLED = ENABLED
channel-type.openwebnet.scenarioEvent.label = Activated Scenario Event
channel-type.openwebnet.scenarioEvent.description = Event corresponding to the number of Basic Scenario that has been activated (read only)
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_01 = Scenario 1
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_02 = Scenario 2
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_03 = Scenario 3
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_04 = Scenario 4
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_05 = Scenario 5
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_06 = Scenario 6
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_07 = Scenario 7
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_08 = Scenario 8
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_09 = Scenario 9
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_10 = Scenario 10
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_11 = Scenario 11
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_12 = Scenario 12
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_13 = Scenario 13
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_14 = Scenario 14
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_15 = Scenario 15
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_16 = Scenario 16
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_17 = Scenario 17
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_18 = Scenario 18
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_19 = Scenario 19
# channel-type.openwebnet.scenarioEvent.event.option.SCENARIO_20 = Scenario 20
channel-type.openwebnet.scenarioProgramCentralUnit.label = Scenario Program Number
channel-type.openwebnet.scenarioProgramCentralUnit.description = Set scenario program number for the Central Unit, valid only with Central Unit mode = "SCENARIO" (read/write)
channel-type.openwebnet.scenarioProgramCentralUnit.state.option.1 = Program 1
@@ -289,6 +276,12 @@ channel-type.openwebnet.weeklyProgramCentralUnit.description = Set weekly progra
channel-type.openwebnet.weeklyProgramCentralUnit.state.option.1 = Program 1
channel-type.openwebnet.weeklyProgramCentralUnit.state.option.2 = Program 2
channel-type.openwebnet.weeklyProgramCentralUnit.state.option.3 = Program 3
channel-type.openwebnet.zoneAlarm.label = Zone Alarm
channel-type.openwebnet.zoneAlarm.description = Current alarm for the zone (read only).
channel-type.openwebnet.zoneAlarm.state.option.INTRUSION = Intrusion
channel-type.openwebnet.zoneAlarm.state.option.TAMPERING = Tampering
channel-type.openwebnet.zoneAlarm.state.option.ANTI_PANIC = Anti Panic
channel-type.openwebnet.zoneAlarm.state.option.SILENT = Silent
# thing status descriptions

View File

@@ -0,0 +1,41 @@
<?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 Alarm System -->
<thing-type id="bus_alarm_system">
<supported-bridge-type-refs>
<bridge-type-ref id="bus_gateway"/>
</supported-bridge-type-refs>
<label>Alarm System</label>
<description>A OpenWebNet BUS/SCS alarm system.</description>
<channels>
<!-- read only -->
<channel id="state" typeId="alarmSystemState"/>
<channel id="armed" typeId="armedState"/>
<channel id="network" typeId="alarmNetwork"/>
<channel id="battery" typeId="alarmBattery"/>
</channels>
<properties>
<property name="vendor">BTicino/Legrand</property>
<property name="model">BTicino Burglar-alarm Unit 3486</property>
<property name="ownDeviceType">5100</property>
</properties>
<representation-property>ownId</representation-property>
<config-description>
<parameter name="where" type="text" readOnly="true">
<label>OpenWebNet Address (where)</label>
<description>The alarm system has no where address (set where to 0 as default).</description>
<default>0</default>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -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 BUS Alarm Zone -->
<thing-type id="bus_alarm_zone">
<supported-bridge-type-refs>
<bridge-type-ref id="bus_gateway"/>
</supported-bridge-type-refs>
<label>Alarm Zone</label>
<description>A OpenWebNet BUS/SCS configured alarm zone.</description>
<channels>
<!-- read only -->
<channel id="state" typeId="alarmZoneState"/>
<channel id="alarm" typeId="zoneAlarm"/>
</channels>
<properties>
<property name="vendor">BTicino/Legrand</property>
<property name="model">Alarm zone as configured in the Alarm System Unit</property>
<property name="ownDeviceType">5200</property>
</properties>
<representation-property>ownId</representation-property>
<config-description>
<parameter name="where" type="text" required="true">
<label>OpenWebNet Address (where)</label>
<description>Example: alarm zone 2 --> where=2.</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -409,4 +409,59 @@
<state readOnly="true"></state>
</channel-type>
<!-- Alarm channels -->
<channel-type id="alarmSystemState">
<item-type>Switch</item-type>
<label>System State</label>
<description>Alarm system is active (ON) or inactive (OFF) (read only).</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="armedState">
<item-type>Switch</item-type>
<label>Armed State</label>
<description>Alarm is armed (ON) or disarmed (OFF) (read only).</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="alarmNetwork">
<item-type>Switch</item-type>
<label>Network State</label>
<description>Alarm system network state (ON = network OK, OFF = no network) (read only).</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="alarmBattery">
<item-type>String</item-type>
<label>Battery State</label>
<description>Alarm system battery state (read only).</description>
<state readOnly="true">
<options>
<option value="OK">Ok</option>
<option value="FAULT">Fault</option>
<option value="UNLOADED">Unloaded</option>
</options>
</state>
</channel-type>
<channel-type id="zoneAlarm">
<item-type>String</item-type>
<label>Zone Alarm</label>
<description>Current alarm for the zone (read only).</description>
<state readOnly="true">
<options>
<option value="INTRUSION">Intrusion</option>
<option value="TAMPERING">Tampering</option>
<option value="ANTI_PANIC">Anti Panic</option>
<option value="SILENT">Silent</option>
</options>
</state>
</channel-type>
<channel-type id="alarmZoneState">
<item-type>Switch</item-type>
<label>Alarm Zone State</label>
<description>Alarm zone is active (ON) or inactive (OFF) (read only).</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -22,6 +22,7 @@ import org.openhab.core.thing.Bridge;
import org.openwebnet4j.message.BaseOpenMessage;
import org.openwebnet4j.message.FrameException;
import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereAlarm;
import org.openwebnet4j.message.WhereAuxiliary;
import org.openwebnet4j.message.WhereCEN;
import org.openwebnet4j.message.WhereEnergyManagement;
@@ -33,11 +34,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test class for {@link OpenWebNetBridgeHandler#ownID} and ThingID
* calculation using {@link OpenWebNetBridgeHandler}
* methods: normalizeWhere(), ownIdFromWhoWhere(), ownIdFromMessage(), thingIdFromWhere()
* Test class for {@link OpenWebNetBridgeHandler#ownID} and ThingID calculation
* using {@link OpenWebNetBridgeHandler} methods: normalizeWhere(),
* ownIdFromWhoWhere(), ownIdFromMessage(), thingIdFromWhere()
*
* @author Massimo Valla - Initial contribution
* @author Massimo Valla - Initial contribution, updates
* @author Andrea Conte - Energy management
* @author Giovanni Fabiani - Auxiliary message support
*/
@@ -48,7 +49,6 @@ public class OwnIdTest {
// @formatter:off
/**
*
* deviceWhere
* (DevAddrParam)
* TYPE WHERE = normalizeWhere() ownId ThingID
@@ -68,29 +68,34 @@ public class OwnIdTest {
* BUS DryContact 399 399 25.399 399
* BUS AUX 4 4 9.4 4
* BUS Scenario 05 05 0.05 05
* BUS Alarm Zone #2 or 2 2 5.2 2
* BUS Alarm silent 0 0 5.0 0
* BUS Alarm system null 0 5.0 0
*/
// @formatter:on
public enum TEST {
// @formatter:off
zb_switch(new WhereZigBee("789309801#9"), Who.fromValue(1), "*1*1*789309801#9##", "789309800h9", "1.789309800h9", "789309800h9"),
zb_switch_2u_1(new WhereZigBee("789301201#9"), Who.fromValue(1), "*1*1*789301201#9##", "789301200h9", "1.789301200h9", "789301200h9"),
zb_switch_2u_2(new WhereZigBee("789301202#9"), Who.fromValue(1), "*1*1*789301202#9##", "789301200h9", "1.789301200h9", "789301200h9"),
bus_switch(new WhereLightAutom("51"), Who.fromValue(1), "*1*1*51##", "51", "1.51", "51"),
bus_localbus(new WhereLightAutom("25#4#01"), Who.fromValue(1), "*1*1*25#4#01##", "25h4h01", "1.25h4h01", "25h4h01"),
bus_autom(new WhereLightAutom("93"), Who.fromValue(2), "*2*0*93##", "93", "2.93", "93"),
bus_thermo_via_cu(new WhereThermo("#1"), Who.fromValue(4),"*#4*#1*0*0020##" ,"1", "4.1", "1"),
bus_thermo(new WhereThermo("1"), Who.fromValue(4),"*#4*1*0*0020##" , "1", "4.1", "1"),
bus_thermo_act(new WhereThermo("1#2"), Who.fromValue(4),"*#4*1#2*20*0##" ,"1", "4.1", "1"),
bus_tempSensor(new WhereThermo("500"), Who.fromValue(4), "*#4*500*15*1*0020*0001##", "500", "4.500", "500"),
bus_energy(new WhereEnergyManagement("51"), Who.fromValue(18), "*#18*51*113##", "51", "18.51", "51"),
bus_cen(new WhereCEN("51"), Who.fromValue(15), "*15*31*51##", "51", "15.51", "51"),
bus_cen_plus(new WhereCEN("212"), Who.fromValue(25), "*25*21#31*212##", "212", "25.212", "212"),
bus_drycontact(new WhereCEN("399"), Who.fromValue(25), "*25*32#1*399##", "399", "25.399", "399"),
bus_aux(new WhereAuxiliary("4"), Who.fromValue(9), "*9*1*4##","4","9.4","4"),
bus_scenario(new WhereLightAutom("05"), Who.fromValue(0), "*0*2*05##","05","0.05","05");
// msg, who, where, normW, ownId, thingId
zb_switch("*1*1*789309801#9##", Who.fromValue(1), new WhereZigBee("789309801#9"), "789309800h9", "1.789309800h9", "789309800h9"),
zb_switch_2u_1("*1*1*789301201#9##", Who.fromValue(1), new WhereZigBee("789301201#9"), "789301200h9", "1.789301200h9", "789301200h9"),
zb_switch_2u_2("*1*1*789301202#9##", Who.fromValue(1), new WhereZigBee("789301202#9"), "789301200h9", "1.789301200h9", "789301200h9"),
bus_switch("*1*1*51##", Who.fromValue(1), new WhereLightAutom("51"),"51", "1.51", "51"),
bus_localbus("*1*1*25#4#01##", Who.fromValue(1), new WhereLightAutom("25#4#01"), "25h4h01", "1.25h4h01", "25h4h01"),
bus_autom("*2*0*93##",Who.fromValue(2), new WhereLightAutom("93"), "93", "2.93", "93"),
bus_thermo_via_cu("*#4*#1*0*0020##", Who.fromValue(4), new WhereThermo("#1"), "1", "4.1", "1"),
bus_thermo("*#4*1*0*0020##", Who.fromValue(4), new WhereThermo("1"), "1", "4.1", "1"),
bus_thermo_act("*#4*1#2*20*0##", Who.fromValue(4), new WhereThermo("1#2") ,"1", "4.1", "1"),
bus_tempSensor("*#4*500*15*1*0020*0001##",Who.fromValue(4), new WhereThermo("500"), "500", "4.500", "500"),
bus_energy("*#18*51*113##", Who.fromValue(18), new WhereEnergyManagement("51"), "51", "18.51", "51"),
bus_cen("*15*31*51##", Who.fromValue(15), new WhereCEN("51"), "51", "15.51", "51"),
bus_cen_plus("*25*21#31*212##", Who.fromValue(25), new WhereCEN("212"), "212", "25.212", "212"),
bus_drycontact("*25*32#1*399##", Who.fromValue(25), new WhereCEN("399"), "399", "25.399", "399"),
bus_aux( "*9*1*4##", Who.fromValue(9), new WhereAuxiliary("4"),"4","9.4","4"),
bus_scenario( "*0*2*05##", Who.fromValue(0), new WhereLightAutom("05"), "05","0.05","05"),
bus_alarm_zh("*#5*#2##", Who.fromValue(5), new WhereAlarm("#2"), "2", "5.2", "2"),
bus_alarm_silent("*5*2*0##", Who.fromValue(5), new WhereAlarm("0"), "0", "5.0", "0"),
bus_alarm_system( "*5*7*##", Who.fromValue(5),new WhereAlarm("0"), "0", "5.0", "0");
// @formatter:on
@@ -101,7 +106,7 @@ public class OwnIdTest {
public final @Nullable BaseOpenMessage msg;
public final String norm, ownId, thingId;
private TEST(Where where, Who who, String msg, String norm, String ownId, String thingId) {
private TEST(String msg, Who who, Where where, String norm, String ownId, String thingId) {
this.where = where;
this.who = who;
BaseOpenMessage bmsg = null;
@@ -125,7 +130,7 @@ public class OwnIdTest {
BaseOpenMessage bmsg;
for (int i = 0; i < TEST.values().length; i++) {
TEST test = TEST.values()[i];
logger.info("testing where={}", test.where);
logger.info("testing {} (who={} where={})", test.msg, test.who, test.where);
assertEquals(test.norm, brH.normalizeWhere(test.where));
assertEquals(test.ownId, brH.ownIdFromWhoWhere(test.who, test.where));
bmsg = test.msg;