Skip to content

Commit

Permalink
[openwebnet] add support for lights general/area/group events and Thi…
Browse files Browse the repository at this point in the history
…ngs (#15921)

* [openwebnet] initial support for general/area light events

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
  • Loading branch information
mvalla committed May 13, 2024
1 parent 8b85ff1 commit 575c86c
Show file tree
Hide file tree
Showing 20 changed files with 731 additions and 84 deletions.
69 changes: 45 additions & 24 deletions bundles/org.openhab.binding.openwebnet/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
* The {@link OpenWebNetBindingConstants} class defines common constants, which
* are used across the whole binding.
*
* @author Massimo Valla - Initial contribution, updates
* @author Massimo Valla - Initial contribution, updates. Light groups.
* @author Gilberto Cocchi - Thermoregulation
* @author Andrea Conte - Energy management, Thermoregulation
* @author Giovanni Fabiani - Aux support
* @author Giovanni Fabiani - AUX support
*/

@NonNullByDefault
Expand All @@ -53,6 +53,10 @@ public class OpenWebNetBindingConstants {
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_LIGHT_GROUP = new ThingTypeUID(BINDING_ID, "bus_light_group");
public static final String THING_LABEL_BUS_LIGHT_GROUP = "Light Group";

public static final ThingTypeUID THING_TYPE_BUS_AUTOMATION = new ThingTypeUID(BINDING_ID, "bus_automation");
public static final String THING_LABEL_BUS_AUTOMATION = "Automation";
public static final ThingTypeUID THING_TYPE_BUS_ENERGY_METER = new ThingTypeUID(BINDING_ID, "bus_energy_meter");
Expand Down Expand Up @@ -97,6 +101,7 @@ public class OpenWebNetBindingConstants {
public static final Set<ThingTypeUID> LIGHTING_SUPPORTED_THING_TYPES = Set.of(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);
public static final Set<ThingTypeUID> LIGHTING_GROUP_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_LIGHT_GROUP);
// ## Automation
public static final Set<ThingTypeUID> AUTOMATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_AUTOMATION,
THING_TYPE_BUS_AUTOMATION);
Expand All @@ -118,7 +123,7 @@ public class OpenWebNetBindingConstants {

// ## Groups
public static final Set<ThingTypeUID> DEVICE_SUPPORTED_THING_TYPES = Stream
.of(LIGHTING_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES,
.of(LIGHTING_SUPPORTED_THING_TYPES, LIGHTING_GROUP_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,
ALARM_SUPPORTED_THING_TYPES, GENERIC_SUPPORTED_THING_TYPES)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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.OpenWebNetLightingGroupHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetLightingHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioBasicHandler;
import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
Expand Down Expand Up @@ -66,6 +67,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
} else if (OpenWebNetLightingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW LIGHTING Handler --- {}", thing.getUID());
return new OpenWebNetLightingHandler(thing);
} else if (OpenWebNetLightingGroupHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW LIGHTING GROUP Handler --- {}", thing.getUID());
return new OpenWebNetLightingGroupHandler(thing);
} else if (OpenWebNetAutomationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW AUTOMATION Handler --- {}", thing.getUID());
return new OpenWebNetAutomationHandler(thing);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.openwebnet4j.message.BaseOpenMessage;
import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereAlarm;
import org.openwebnet4j.message.WhereLightAutom;
import org.openwebnet4j.message.WhereThermo;
import org.openwebnet4j.message.WhereZigBee;
import org.openwebnet4j.message.Who;
Expand All @@ -41,7 +42,7 @@
* The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering
* OpenWebNet devices connected to a bridge/gateway
*
* @author Massimo Valla - Initial contribution
* @author Massimo Valla - Initial contribution. Discovery of BUS light Group
* @author Andrea Conte - Energy management, Thermoregulation
* @author Gilberto Cocchi - Thermoregulation
* @author Giovanni Fabiani - Aux support
Expand All @@ -56,7 +57,8 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov

private @NonNullByDefault({}) ThingUID bridgeUID;

private boolean cuFound = false;
private boolean thermoCUFound = false;
private boolean lightFound = false;

public OpenWebNetDeviceDiscoveryService() {
super(OpenWebNetBridgeHandler.class, SUPPORTED_THING_TYPES, SEARCH_TIME_SEC);
Expand All @@ -71,7 +73,9 @@ public Set<ThingTypeUID> getSupportedThingTypes() {
protected void startScan() {
logger.info("------ SEARCHING for DEVICES on bridge '{}' ({}) ...", thingHandler.getThing().getLabel(),
bridgeUID);
cuFound = false;

thermoCUFound = false;
lightFound = false;
thingHandler.searchDevices();
}

Expand Down Expand Up @@ -223,14 +227,20 @@ public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,
}

String ownId = thingHandler.ownIdFromWhoWhere(deviceWho, w);
if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)) {
if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)
|| OpenWebNetBindingConstants.THING_TYPE_BUS_DIMMER.equals(thingTypeUID)) {
WhereLightAutom wla = (WhereLightAutom) w;
discoverBUSGroups(deviceWho, wla);
if (wla.isGroup()) { // group thing has been already discovered in previous line
return;
}
if (thingHandler.getRegisteredDevice(ownId) != null) {
logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", w);
return;
}
}

String tId = thingHandler.thingIdFromWhere(w);
String tId = thingHandler.thingIdFromWhoWhere(deviceWho, w);
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);

DiscoveryResult discoveryResult = null;
Expand All @@ -246,7 +256,7 @@ public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,

// detect Thermo CU type
if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingTypeUID)) {
cuFound = true;
thermoCUFound = true;
logger.debug("CU found: {}", w);
if (w.value().charAt(0) == '#') { // 99-zone CU
thingLabel += " 99-zone";
Expand All @@ -258,7 +268,7 @@ public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,
logger.debug("@@@@ THERMO CU found 4-zone: where={}, ownId={}, whereConfig={}", w, ownId, whereConfig);
}
} else if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE.equals(thingTypeUID)) {
if (cuFound) {
if (thermoCUFound) {
// set param standalone = false for thermo zone
properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE, false);
}
Expand Down Expand Up @@ -292,6 +302,48 @@ public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,
thingDiscovered(discoveryResult);
}

private void discoverBUSGroups(Who deviceWho, WhereLightAutom where) {
if (!lightFound) {
lightFound = true;
createGroupDiscoveryResult(deviceWho, (WhereLightAutom) WhereLightAutom.GENERAL);
}
if (where.isGroup()) {
createGroupDiscoveryResult(deviceWho, where);
} else {
createGroupDiscoveryResult(deviceWho, new WhereLightAutom(Integer.toString(where.getArea())));
}
}

private void createGroupDiscoveryResult(Who deviceWho, WhereLightAutom where) {
Map<String, Object> properties = new HashMap<>(2);
String ownId = thingHandler.ownIdFromWhoWhere(deviceWho, where);
String tId = thingHandler.thingIdFromWhoWhere(deviceWho, where);

String thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_LIGHT_GROUP;

if (where.isGeneral()) {
thingLabel = "General " + thingLabel;
} else if (where.isArea()) {
thingLabel = "Area " + where.getArea() + " " + thingLabel;
} else {
thingLabel += " " + where.value();
}

ThingUID thingUID = new ThingUID(OpenWebNetBindingConstants.THING_TYPE_BUS_LIGHT_GROUP, bridgeUID, tId);
String whereConfig = where.value();

properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, whereConfig);
properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, ownId);

thingLabel = thingLabel + " (WHERE=" + whereConfig + ")";

DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withThingType(OpenWebNetBindingConstants.THING_TYPE_BUS_LIGHT_GROUP).withProperties(properties)
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)
.withLabel(thingLabel).build();
thingDiscovered(discoveryResult);
}

@Override
public void initialize() {
thingHandler.deviceDiscoveryService = this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* Copyright (c) 2010-2024 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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openwebnet4j.message.WhereLightAutom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A map to store handlers for lights and automations. The map is organised by
* Area.
*
* @author Massimo Valla - Initial contribution
*/
@NonNullByDefault
public class LightAutomHandlersMap {

private final Logger logger = LoggerFactory.getLogger(LightAutomHandlersMap.class);

private Map<Integer, Map<String, OpenWebNetThingHandler>> hndlrsMap;
private @Nullable OpenWebNetThingHandler oneHandler = null;

protected LightAutomHandlersMap() {
hndlrsMap = new ConcurrentHashMap<>();
}

protected void add(int area, OpenWebNetThingHandler handler) {
if (!hndlrsMap.containsKey(area)) {
hndlrsMap.put(area, new ConcurrentHashMap<>());
}
Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(Integer.valueOf(area));
final String handlerOwnId = handler.ownId;
if (areaHndlrs != null && handlerOwnId != null) {
areaHndlrs.put(handlerOwnId, handler);
if (oneHandler == null) {
oneHandler = handler;
}
logger.debug("MAP - Added handler {} to Area {}", handlerOwnId, area);
logger.debug("Map: {}", this.toString());
}
}

protected void remove(int area, OpenWebNetThingHandler handler) {
if (hndlrsMap.containsKey(area)) {
Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(Integer.valueOf(area));
if (areaHndlrs != null) {
boolean removed = areaHndlrs.remove(handler.ownId, handler);
OpenWebNetThingHandler oneHandler = this.oneHandler;
// if the removed handler was linked by oneHandler, find another one
if (removed && oneHandler != null && oneHandler.equals(handler)) {
this.oneHandler = getFirst();
}
logger.debug("MAP - Removed handler {} from Area {}", handler.ownId, area);
logger.debug("Map: {}", this.toString());
}
}
}

protected @Nullable List<OpenWebNetThingHandler> getAreaHandlers(int area) {
Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(area);
if (areaHndlrs != null) {
List<OpenWebNetThingHandler> list = new ArrayList<OpenWebNetThingHandler>(areaHndlrs.values());
return list;
} else {
return null;
}
}

protected @Nullable List<OpenWebNetThingHandler> getAllHandlers() {
List<OpenWebNetThingHandler> list = new ArrayList<OpenWebNetThingHandler>();
for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
OpenWebNetThingHandler hndlr = innerEntry.getValue();
list.add(hndlr);
}
}
return list;
}

protected boolean isEmpty() {
return oneHandler == null;
}

protected @Nullable OpenWebNetThingHandler getOneHandler() {
if (oneHandler == null) {
oneHandler = getFirst();
}
return oneHandler;
}

private @Nullable OpenWebNetThingHandler getFirst() {
for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
OpenWebNetThingHandler thingHandler = innerEntry.getValue();
WhereLightAutom whereLightAutom = (WhereLightAutom) thingHandler.deviceWhere;
if (whereLightAutom != null && whereLightAutom.isAPL()) {
return thingHandler;
}
}
}
return null;
}

@Override
public String toString() {
StringBuilder log = new StringBuilder();
log.append("\n---- LightAutomHandlersMap ----");
for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
log.append("\n- Area: " + entry.getKey() + "\n -");
Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
OpenWebNetThingHandler thingHandler = innerEntry.getValue();
log.append(" " + thingHandler.ownId);
}
}
log.append("\n# getAllHandlers: ");
List<OpenWebNetThingHandler> allHandlers = getAllHandlers();
if (allHandlers != null) {
for (OpenWebNetThingHandler singleHandler : allHandlers) {
log.append(" " + singleHandler.ownId);
}
}
OpenWebNetThingHandler oneThingHandler = this.getOneHandler();
log.append("\n# getOneHandler() = " + (oneThingHandler == null ? "null" : oneThingHandler.ownId));
log.append("\n-------------------------------");

return log.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
}

@Override
protected String ownIdPrefix() {
return Who.BURGLAR_ALARM.value().toString();
protected Who getManagedWho() {
return Who.BURGLAR_ALARM;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ private void handlePercentCommand(PercentType command, String w) {
}

@Override
protected String ownIdPrefix() {
return Who.AUTOMATION.value().toString();
protected Who getManagedWho() {
return Who.AUTOMATION;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
}

@Override
protected String ownIdPrefix() {
return Who.AUX.value().toString();
protected Who getManagedWho() {
return Who.AUX;
}
}

0 comments on commit 575c86c

Please sign in to comment.