Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[unifi] Provide LED channels for access point #17534

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions bundles/org.openhab.binding.unifi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,18 @@ The default mode value is `auto`.

The `accessPoint` information that is retrieved is available as these channels:

| Channel ID | Item Type | Description | Permissions |
|------------|----------------------|----------------------------------------------------------------------|-------------|
| online | Switch | Online status of the device | Read |
| enable | Switch | Enable or disable the access point | Read, Write |
| name | String | Name of device (from the controller web UI) | Read |
| site | String | Site name (from the controller web UI) the device is associated with | Read |
| ipAddress | String | IP address of the device | Read |
| uptime | Number:Time | Uptime of the device (in seconds) | Read |
| lastSeen | DateTime | Date and Time the device was last seen | Read |
| experience | Number:Dimensionless | The average health indication of the connected clients | Read |
| Channel ID | Item Type | Description | Permissions |
|-------------|----------------------|----------------------------------------------------------------------|-------------|
| online | Switch | Online status of the device | Read |
| enable | Switch | Enable or disable the access point | Read, Write |
| name | String | Name of device (from the controller web UI) | Read |
| site | String | Site name (from the controller web UI) the device is associated with | Read |
| ipAddress | String | IP address of the device | Read |
| uptime | Number:Time | Uptime of the device (in seconds) | Read |
| lastSeen | DateTime | Date and Time the device was last seen | Read |
| experience | Number:Dimensionless | The average health indication of the connected clients | Read |
| ledOverride | Switch | Override the LED. If disabled, the site-wide setting is used | Read, Write |
| ledColor | Color | Controls the color and brightness of the LED (when overridden) | Read, Write |

## Rule Actions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public final class UniFiBindingConstants {
// List of access point device channels
public static final String CHANNEL_AP_ENABLE = "enable";
public static final String CHANNEL_AP_STATE = "state";
public static final String CHANNEL_AP_LED_OVERRIDE = "ledOverride";
public static final String CHANNEL_AP_LED_COLOR = "ledColor";

// List of all Parameters
public static final String PARAMETER_HOST = "host";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,24 @@ public void disableAccessPoint(final UniFiDevice device, final boolean disable)
refresh();
}

public void setLedOverride(final UniFiDevice device, final String override, final String color,
final @Nullable Integer brightness) throws UniFiException {
final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.PUT, gson);
req.setAPIPath(String.format("/api/s/%s/rest/device/%s", device.getSite().getName(), device.getId()));
req.setBodyParameter("_id", device.getId());
if (!override.isEmpty()) {
req.setBodyParameter("led_override", override);
}
if (!color.isEmpty()) {
req.setBodyParameter("led_override_color", color);
}
if (brightness != null) {
req.setBodyParameter("led_override_color_brightness", brightness);
}
executeRequest(req);
refresh();
}

public void generateVouchers(final UniFiSite site, final int count, final int expiration, final int users,
@Nullable Integer upLimit, @Nullable Integer downLimit, @Nullable Integer dataQuota) throws UniFiException {
final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public class UniFiDevice implements HasId {

private Boolean disabled;

private String ledOverride;

private String ledOverrideColor;

private Integer ledOverrideColorBrightness;

public UniFiDevice(final UniFiControllerCache cache) {
this.cache = cache;
}
Expand Down Expand Up @@ -138,6 +144,18 @@ public Boolean isDisabled() {
return disabled;
}

public String getLedOverride() {
return ledOverride;
}

public String getLedOverrideColor() {
return ledOverrideColor;
}

public Integer getLedOverrideColorBrightness() {
return ledOverrideColorBrightness;
}

@Override
public String toString() {
return String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*;

import java.awt.Color;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map;
Expand All @@ -27,7 +28,10 @@
import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
Expand All @@ -38,6 +42,9 @@
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.core.util.ColorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An access point managed by the UniFi controller software.
Expand All @@ -47,6 +54,10 @@
@NonNullByDefault
public class UniFiAccessPointThingHandler extends UniFiBaseThingHandler<UniFiDevice, UniFiAccessPointThingConfig> {

private static final int LED_BRIGHTNESS_STEP_PERCENT = 5;

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

private UniFiAccessPointThingConfig config = new UniFiAccessPointThingConfig();

public UniFiAccessPointThingHandler(final Thing thing) {
Expand Down Expand Up @@ -152,10 +163,30 @@ protected State getChannelState(final UniFiDevice device, final String channelId
state = new QuantityType<>(device.getExperience(), Units.PERCENT);
}
break;
case CHANNEL_AP_LED_OVERRIDE:
state = OnOffType.from(!"default".equals(device.getLedOverride()));
break;
case CHANNEL_AP_LED_COLOR:
state = getLedColorState(device);
break;
}
return state;
}

private State getLedColorState(final UniFiDevice device) {
if ("off".equals(device.getLedOverride())) {
return HSBType.BLACK;
}
String overrideColor = device.getLedOverrideColor();
Integer overrideBrightness = device.getLedOverrideColorBrightness();
if (overrideColor == null || overrideBrightness == null) {
return UnDefType.UNDEF;
}
Color color = Color.decode(overrideColor);
HSBType hsb = HSBType.fromRGB(color.getRed(), color.getGreen(), color.getBlue());
return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(overrideBrightness));
}

@Override
protected void updateProperties(final UniFiDevice device) {
updateProperties(Map.of( //
Expand All @@ -171,6 +202,10 @@ protected boolean handleCommand(final UniFiController controller, final UniFiDev

if (CHANNEL_AP_ENABLE.equals(channelID) && command instanceof OnOffType onOffCommand) {
return handleEnableCommand(controller, device, channelUID, onOffCommand);
} else if (CHANNEL_AP_LED_OVERRIDE.equals(channelID) && command instanceof OnOffType onOffCommand) {
return handleLedOverrideCommand(controller, device, channelUID, onOffCommand);
} else if (CHANNEL_AP_LED_COLOR.equals(channelID)) {
return handleLedColorCommand(controller, device, channelUID, command);
}
return false;
}
Expand All @@ -181,4 +216,59 @@ private boolean handleEnableCommand(final UniFiController controller, final UniF
refresh();
return true;
}

private boolean handleLedOverrideCommand(final UniFiController controller, final UniFiDevice device,
final ChannelUID channelUID, final OnOffType command) throws UniFiException {
controller.setLedOverride(device, command == OnOffType.ON ? "on" : "default", "", null);
refresh();
return true;
}

private boolean handleLedColorCommand(final UniFiController controller, final UniFiDevice device,
final ChannelUID channelUID, final Command command) throws UniFiException {
String newOverride = "", newColor = "";
Integer newBrightness = null;

if (command instanceof HSBType hsbCommand) {
int brightness = hsbCommand.getBrightness().intValue();
HSBType fullColor = new HSBType(hsbCommand.getHue(), hsbCommand.getSaturation(), PercentType.HUNDRED);
Color color = new Color(ColorUtil.hsbTosRgb(fullColor));
newOverride = brightness == 0 ? "off" : "on";
newBrightness = brightness;
newColor = "#%02x%02x%02x".formatted(color.getRed(), color.getGreen(), color.getBlue());
} else if (command instanceof PercentType brightnessCommand) {
int brightness = brightnessCommand.intValue();
newOverride = brightness == 0 ? "off" : "on";
newBrightness = brightness;
} else {
Integer currentOverrideBrightness = device.getLedOverrideColorBrightness();
if (currentOverrideBrightness == null) {
logger.warn("Failed to handle command {} since current state is missing", command);
return false;
}
int currentBrightness = currentOverrideBrightness.intValue();

if (command instanceof IncreaseDecreaseType increaseDecreaseCommand) {
int brightness = switch (increaseDecreaseCommand) {
case INCREASE -> currentBrightness + LED_BRIGHTNESS_STEP_PERCENT;
case DECREASE -> currentBrightness - LED_BRIGHTNESS_STEP_PERCENT;
};
brightness = brightness < 0 ? 0 : brightness > 100 ? 100 : brightness;
newOverride = brightness == 0 ? "off" : "on";
newBrightness = brightness;
} else if (command instanceof OnOffType) {
newOverride = command == OnOffType.ON ? "on" : "off";
newBrightness = command == OnOffType.ON && currentBrightness == 0 ? 100 : null;
}
}

boolean isOverridden = !"default".equals(device.getLedOverride());
if ((!newOverride.isEmpty() && isOverridden) || !newColor.isEmpty() || newBrightness != null) {
controller.setLedOverride(device, isOverridden ? newOverride : "", newColor, newBrightness);
refresh();
return true;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ thing-type.unifi.accessPoint.description = An access point managed by a UniFi co
thing-type.unifi.accessPoint.channel.experience.description = The average experience of the connected clients
thing-type.unifi.accessPoint.channel.ipAddress.description = IP address of the device
thing-type.unifi.accessPoint.channel.lastSeen.description = Timestamp of when the device was last seen
thing-type.unifi.accessPoint.channel.ledColor.description = Controls the color and brightness of the LED
thing-type.unifi.accessPoint.channel.name.description = Name of the device
thing-type.unifi.accessPoint.channel.online.description = Online status of the device
thing-type.unifi.accessPoint.channel.uptime.description = Uptime of the device (in seconds)
Expand Down Expand Up @@ -65,6 +66,8 @@ channel-type.unifi.ap.label = Access Point
channel-type.unifi.ap.description = Access Point the wireless client is connected to
channel-type.unifi.apEnable.label = Enabled
channel-type.unifi.apEnable.description = If the access point is enabled
channel-type.unifi.apLedOverride.label = LED Override
channel-type.unifi.apLedOverride.description = If the LED is overridden
channel-type.unifi.blocked.label = Blocked
channel-type.unifi.blocked.description = Is device blocked
channel-type.unifi.devState.label = Device State
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,15 @@
<channel id="experience" typeId="experience">
<description>The average experience of the connected clients</description>
</channel>
<channel id="ledOverride" typeId="apLedOverride"/>
<channel id="ledColor" typeId="system.color">
<description>Controls the color and brightness of the LED</description>
</channel>
</channels>

<properties>
<property name="vendor">Ubiquiti Networks</property>
<property name="thingTypeVersion">1</property>
<property name="thingTypeVersion">2</property>
</properties>

<config-description-ref uri="thing-type:unifi:accessPoint"/>
Expand Down Expand Up @@ -487,4 +491,10 @@
</state>
</channel-type>

<channel-type id="apLedOverride">
<item-type>Switch</item-type>
<label>LED Override</label>
<description>If the LED is overridden</description>
</channel-type>

</thing:thing-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
<description>The average experience of the connected clients</description>
</add-channel>
</instruction-set>
<instruction-set targetVersion="2">
<add-channel id="ledOverride">
<type>unifi:ledOverride</type>
</add-channel>
<add-channel id="ledColor">
<type>system:color</type>
<description>Controls the color and brightness of the LED</description>
</add-channel>
</instruction-set>
</thing-type>

</update:update-descriptions>