Skip to content

Commit

Permalink
[velbus] Add modules
Browse files Browse the repository at this point in the history
Modules : VMB4LEDPWM-20, VMB8DC-20

Signed-off-by: Daniel Rosengarten <[email protected]>
  • Loading branch information
Rosen01 committed Jun 11, 2024
1 parent 15a98c9 commit 50248f9
Show file tree
Hide file tree
Showing 11 changed files with 623 additions and 75 deletions.
160 changes: 89 additions & 71 deletions bundles/org.openhab.binding.velbus/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public class VelbusBindingConstants {
public static final ThingTypeUID THING_TYPE_VMBDALI_20 = new ThingTypeUID(BINDING_ID, "vmbdali-20");
public static final ThingTypeUID THING_TYPE_VMBEL4PIR_20 = new ThingTypeUID(BINDING_ID, "vmbel4pir-20");
public static final ThingTypeUID THING_TYPE_VMBGP4PIR_20 = new ThingTypeUID(BINDING_ID, "vmbgp4pir-20");
public static final ThingTypeUID THING_TYPE_VMB4LEDPWM_20 = new ThingTypeUID(BINDING_ID, "vmb4ledpwm-20");
public static final ThingTypeUID THING_TYPE_VMB8DC_20 = new ThingTypeUID(BINDING_ID, "vmb8dc-20");

// thing type sets
public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Set.of(BRIDGE_THING_TYPE,
Expand All @@ -118,13 +120,15 @@ public class VelbusBindingConstants {
THING_TYPE_VMB4RYLD_10, THING_TYPE_VMB4RYNO_10, THING_TYPE_VMB2BLE_10, THING_TYPE_VMB6PB_20,
THING_TYPE_VMBEL1_20, THING_TYPE_VMBEL2_20, THING_TYPE_VMBEL4_20, THING_TYPE_VMBELO_20,
THING_TYPE_VMBGP1_20, THING_TYPE_VMBGP2_20, THING_TYPE_VMBGP4_20, THING_TYPE_VMBGPO_20,
THING_TYPE_VMBDALI_20, THING_TYPE_VMBEL4PIR_20, THING_TYPE_VMBGP4PIR_20);
THING_TYPE_VMBDALI_20, THING_TYPE_VMBEL4PIR_20, THING_TYPE_VMBGP4PIR_20, THING_TYPE_VMB4LEDPWM_20,
THING_TYPE_VMB8DC_20);

// Velbus module types
public static final byte MODULE_TYPE_VMB8PB = 0x01;
public static final byte MODULE_TYPE_VMB1RY = 0x02;
public static final byte MODULE_TYPE_VMB1BL = 0x03;
public static final byte MODULE_TYPE_VMB6IN = 0x05;
public static final byte MODULE_TYPE_VMB4LEDPWM_20 = 0x06;
public static final byte MODULE_TYPE_VMB1DM = 0x07;
public static final byte MODULE_TYPE_VMB4RY = 0x08;
public static final byte MODULE_TYPE_VMB2BL = 0x09;
Expand Down Expand Up @@ -177,6 +181,7 @@ public class VelbusBindingConstants {
public static final byte MODULE_TYPE_VMB4RYLD_10 = 0x48;
public static final byte MODULE_TYPE_VMB4RYNO_10 = 0x49;
public static final byte MODULE_TYPE_VMB2BLE_10 = 0x4A;
public static final byte MODULE_TYPE_VMB8DC_20 = 0x4B;
public static final byte MODULE_TYPE_VMB6PB_20 = 0x4C;
public static final byte MODULE_TYPE_VMBEL1_20 = 0x4F;
public static final byte MODULE_TYPE_VMBEL2_20 = 0x50;
Expand Down Expand Up @@ -266,7 +271,7 @@ public class VelbusBindingConstants {
public static final byte ALL_DALI_CHANNELS = (byte) 0x51;
public static final byte SUB_ADDRESS_DISABLED = (byte) 0xFF;
public static final byte VALUE_UNCHANGED = (byte) 0xFF;
public static final byte DALI_SETTING_ACTUAL_LEVEL = (byte) 0x1A;
public static final byte SETTING_ACTUAL_LEVEL = (byte) 0x1A;
public static final byte DALI_MAX_VALUE = (byte) 0xFE;

// Module properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.openhab.binding.velbus.internal.handler.VelbusBridgeHandler;
import org.openhab.binding.velbus.internal.handler.VelbusDimmerHandler;
import org.openhab.binding.velbus.internal.handler.VelbusNetworkBridgeHandler;
import org.openhab.binding.velbus.internal.handler.VelbusNewDimmerHandler;
import org.openhab.binding.velbus.internal.handler.VelbusRelayHandler;
import org.openhab.binding.velbus.internal.handler.VelbusRelayWithInputHandler;
import org.openhab.binding.velbus.internal.handler.VelbusSensorHandler;
Expand Down Expand Up @@ -110,6 +111,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
thingHandler = new VelbusVMB1TSHandler(thing);
} else if (VelbusVMBDALIHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
thingHandler = new VelbusVMBDALIHandler(thing);
} else if (VelbusNewDimmerHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
thingHandler = new VelbusNewDimmerHandler(thing);
}

return thingHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,14 @@ private void handleModuleTypeCommand(byte[] packet, byte address) {
velbusModule = new VelbusModule(new VelbusModuleAddress(address, 9), moduleType, highByteOfSerialNumber,
lowByteOfSerialNumber, memoryMapVersion, buildYear, buildWeek, THING_TYPE_VMBDALI_20, 81);
break;
case MODULE_TYPE_VMB4LEDPWM_20:
velbusModule = new VelbusModule(new VelbusModuleAddress(address, 0), moduleType, highByteOfSerialNumber,
lowByteOfSerialNumber, memoryMapVersion, buildYear, buildWeek, THING_TYPE_VMB4LEDPWM_20, 4);
break;
case MODULE_TYPE_VMB8DC_20:
velbusModule = new VelbusModule(new VelbusModuleAddress(address, 0), moduleType, highByteOfSerialNumber,
lowByteOfSerialNumber, memoryMapVersion, buildYear, buildWeek, THING_TYPE_VMB8DC_20, 8);
break;
}

if (velbusModule != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/**
* 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.velbus.internal.handler;

import static org.openhab.binding.velbus.internal.VelbusBindingConstants.*;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velbus.internal.VelbusColorChannel;
import org.openhab.binding.velbus.internal.config.VelbusSensorConfig;
import org.openhab.binding.velbus.internal.packets.VelbusNewDimmerRequestPacket;
import org.openhab.binding.velbus.internal.packets.VelbusPacket;
import org.openhab.binding.velbus.internal.packets.VelbusSetColorPacket;
import org.openhab.binding.velbus.internal.packets.VelbusSetDimPacket;
import org.openhab.binding.velbus.internal.packets.VelbusSetScenePacket;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;

/**
* The {@link VelbusNewDimmerHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Daniel Rosengarten - Initial contribution
*/
@NonNullByDefault
public class VelbusNewDimmerHandler extends VelbusSensorWithAlarmClockHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(
Arrays.asList(THING_TYPE_VMB4LEDPWM_20, THING_TYPE_VMB8DC_20));
private @Nullable ScheduledFuture<?> refreshJob;
private @NonNullByDefault({}) VelbusSensorConfig sensorConfig;

private VelbusColorChannel[] colorChannels;

public VelbusNewDimmerHandler(Thing thing) {
super(thing, 0);

colorChannels = new VelbusColorChannel[8];
}

@Override
public void initialize() {
this.sensorConfig = getConfigAs(VelbusSensorConfig.class);

super.initialize();

initializeAutomaticRefresh();
initializeColorChannel();
initializeChannelStates();
}

private void initializeAutomaticRefresh() {
int refreshInterval = this.sensorConfig.refresh;

if (refreshInterval > 0) {
startAutomaticRefresh(refreshInterval);
}
}

private void initializeColorChannel() {
for (int i = 0; i <= 8; i++) {
colorChannels[i] = new VelbusColorChannel();
}
}

private void initializeChannelStates() {
VelbusBridgeHandler velbusBridgeHandler = getVelbusBridgeHandler();
if (velbusBridgeHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
return;
}

sendNewDimmerReadoutRequest(velbusBridgeHandler, ALL_CHANNELS);
}

@Override
public void dispose() {
final ScheduledFuture<?> refreshJob = this.refreshJob;
if (refreshJob != null) {
refreshJob.cancel(true);
this.refreshJob = null;
}
}

private void startAutomaticRefresh(int refreshInterval) {
VelbusBridgeHandler velbusBridgeHandler = getVelbusBridgeHandler();
if (velbusBridgeHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
return;
}

refreshJob = scheduler.scheduleWithFixedDelay(() -> {
sendNewDimmerReadoutRequest(velbusBridgeHandler, ALL_CHANNELS);
}, 0, refreshInterval, TimeUnit.SECONDS);
}

protected void sendNewDimmerReadoutRequest(VelbusBridgeHandler velbusBridgeHandler, byte channel) {
VelbusNewDimmerRequestPacket packet = new VelbusNewDimmerRequestPacket(getModuleAddress().getAddress(),
channel);

byte[] packetBytes = packet.getBytes();
velbusBridgeHandler.sendPacket(packetBytes);
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
super.handleCommand(channelUID, command);

VelbusBridgeHandler velbusBridgeHandler = getVelbusBridgeHandler();
if (velbusBridgeHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
return;
}

byte address = getModuleAddress().getChannelIdentifier(channelUID).getAddress();
byte channel = Integer.valueOf(getModuleAddress().getChannelNumber(channelUID)).byteValue();

if (command instanceof RefreshType) {
if (isColorGroupChannel(channelUID) || isBrightnessGroupChannel(channelUID)
|| isWhiteGroupChannel(channelUID)) {
sendNewDimmerReadoutRequest(velbusBridgeHandler, channel);
}
} else if (isSceneGroupChannel(channelUID)) {
if (command instanceof DecimalType decimalCommand) {
byte scene = decimalCommand.byteValue();

VelbusSetScenePacket packet = new VelbusSetScenePacket(address, channel, scene);
velbusBridgeHandler.sendPacket(packet.getBytes());
} else {
throw new UnsupportedOperationException(
"The command '" + command + "' is not supported on channel '" + channelUID + "'.");
}
} else if (isColorGroupChannel(channelUID) || isBrightnessGroupChannel(channelUID)
|| isWhiteGroupChannel(channelUID)) {
VelbusColorChannel colorChannel = colorChannels[Byte.toUnsignedInt(channel) - 1];

if (isBrightnessGroupChannel(channelUID)) {
if (command instanceof PercentType percentCommand) {
colorChannel.setBrightness(percentCommand);

VelbusSetDimPacket packet = new VelbusSetDimPacket(address, channel);
packet.setDim(colorChannel.getBrightnessVelbus());
packet.setMode((byte) 0x00); // force direct mode -> need an update to permit the selection between
// : direct, fade rate, fade time.
velbusBridgeHandler.sendPacket(packet.getBytes());
} else {
throw new UnsupportedOperationException(
"The command '" + command + "' is not supported on channel '" + channelUID + "'.");
}
} else if (isColorGroupChannel(channelUID)) {
if (command instanceof HSBType hsbCommand) {
colorChannel.setBrightness(hsbCommand);
colorChannel.setColor(hsbCommand);

VelbusSetColorPacket packet = new VelbusSetColorPacket(address, channel);
packet.setBrightness(colorChannel.getBrightnessVelbus());
packet.setColor(colorChannel.getColorVelbus());
velbusBridgeHandler.sendPacket(packet.getBytes());
} else {
throw new UnsupportedOperationException(
"The command '" + command + "' is not supported on channel '" + channelUID + "'.");
}
} else if (isWhiteGroupChannel(channelUID)) {
if (command instanceof PercentType percentCommand) {
colorChannel.setWhite(percentCommand);

VelbusSetColorPacket packet = new VelbusSetColorPacket(address, channel);
packet.setWhite(colorChannel.getWhiteVelbus());
velbusBridgeHandler.sendPacket(packet.getBytes());
} else {
throw new UnsupportedOperationException(
"The command '" + command + "' is not supported on channel '" + channelUID + "'.");
}
}
}
}

private boolean isColorGroupChannel(ChannelUID channelUID) {
return CHANNEL_GROUP_COLOR.equals(channelUID.getGroupId());
}

private boolean isBrightnessGroupChannel(ChannelUID channelUID) {
return CHANNEL_GROUP_BRIGHTNESS.equals(channelUID.getGroupId());
}

private boolean isWhiteGroupChannel(ChannelUID channelUID) {
return CHANNEL_GROUP_WHITE.equals(channelUID.getGroupId());
}

private boolean isSceneGroupChannel(ChannelUID channelUID) {
return CHANNEL_GROUP_SCENE.equals(channelUID.getGroupId());
}

@Override
public boolean onPacketReceived(byte[] packet) {
if (!super.onPacketReceived(packet)) {
return false;
}

if (packet[0] == VelbusPacket.STX && packet.length >= 7) {
byte command = packet[4];
byte setting = packet[6];

if (command == COMMAND_TEMP_SENSOR_SETTINGS_PART1 && setting == SETTING_ACTUAL_LEVEL) {
int channel = Byte.toUnsignedInt(packet[5]);

if (channel >= 1 && channel <= 8) {
VelbusColorChannel colorChannel = colorChannels[channel - 1];

if (packet.length >= 8 && packet.length < 12) {
ChannelUID brightness = new ChannelUID(thing.getUID(), CHANNEL_GROUP_BRIGHTNESS,
CHANNEL + channel);
colorChannel.setBrightness(packet[7]);
updateState(brightness, colorChannel.getBrightnessPercent());
} else if (packet.length >= 12) {
ChannelUID brightness = new ChannelUID(thing.getUID(), CHANNEL_GROUP_BRIGHTNESS,
CHANNEL + channel);
colorChannel.setBrightness(packet[7]);
updateState(brightness, colorChannel.getBrightnessPercent());

ChannelUID color = new ChannelUID(thing.getUID(), CHANNEL_GROUP_COLOR, CHANNEL + channel);
colorChannel.setColor(new byte[] { packet[8], packet[9], packet[10] });
updateState(color, colorChannel.getColorHSB());

ChannelUID white = new ChannelUID(thing.getUID(), CHANNEL_GROUP_WHITE, CHANNEL + channel);
colorChannel.setWhite(packet[11]);
updateState(white, colorChannel.getWhitePercent());
}
}
} else if (command == COMMAND_DIMVALUE_STATUS && packet.length >= 8) {
int channel = Byte.toUnsignedInt(packet[5]);

if (channel >= 1 && channel <= 8) {
VelbusColorChannel colorChannel = colorChannels[channel - 1];

for (int i = 0; i < (packet.length - 8); i++) {
ChannelUID brightness = new ChannelUID(thing.getUID(), CHANNEL_GROUP_BRIGHTNESS,
CHANNEL + (channel + i));
colorChannel.setBrightness(packet[6 + i]);
updateState(brightness, colorChannel.getBrightnessPercent());
}
}
}
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public boolean onPacketReceived(byte[] packet) {
byte command = packet[4];
byte setting = packet[6];

if (command == COMMAND_TEMP_SENSOR_SETTINGS_PART1 && setting == DALI_SETTING_ACTUAL_LEVEL) {
if (command == COMMAND_TEMP_SENSOR_SETTINGS_PART1 && setting == SETTING_ACTUAL_LEVEL) {
int channel = Byte.toUnsignedInt(packet[5]);

if (channel >= 1 && channel <= 80) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected byte[] getDataBytes() {
return new byte[] { COMMAND_TEMP_SENSOR_SETTINGS_REQUEST, this.channel, GATEWAY_CONFIG };
} else {
return new byte[] { COMMAND_TEMP_SENSOR_SETTINGS_REQUEST, this.channel, GATEWAY_CONFIG,
DALI_SETTING_ACTUAL_LEVEL };
SETTING_ACTUAL_LEVEL };
}
}
}
Loading

0 comments on commit 50248f9

Please sign in to comment.