public static final String SERVICE_TYPE = "_nanoleafapi._tcp.local.";
// Effect/scene name for static color
- public static final String EFFECT_NAME_STATIC_COLOR = "*Dynamic*";
+ public static final String EFFECT_NAME_STATIC_COLOR = "*Static*";
+ public static final String EFFECT_NAME_SOLID_COLOR = "*Solid*";
// Color channels increase/decrease brightness step size
public static final int BRIGHTNESS_STEP_SIZE = 5;
--- /dev/null
+/**
+ * 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.nanoleaf.internal.colors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A listener used to notify panels when they change color.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+
+@NonNullByDefault
+public interface NanoleafControllerColorChangeListener {
+
+ /**
+ * This method is called after any panel changes its color.
+ */
+ void onPanelChangedColor();
+}
--- /dev/null
+/**
+ * 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.nanoleaf.internal.colors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.HSBType;
+
+/**
+ * A listener used to notify panels when they change color.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+
+@NonNullByDefault
+public interface NanoleafPanelColorChangeListener {
+
+ /**
+ * This method is called after a panel changes its color
+ *
+ * @param newColor the new color of the panel
+ */
+ void onPanelChangedColor(HSBType newColor);
+}
--- /dev/null
+/**
+ * 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.nanoleaf.internal.colors;
+
+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.openhab.binding.nanoleaf.internal.handler.NanoleafPanelHandler;
+import org.openhab.core.library.types.HSBType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Stores information about panels and their colors, while sending notifications to panels and controllers
+ * about updated states.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+@NonNullByDefault
+public class NanoleafPanelColors {
+
+ private final Logger logger = LoggerFactory.getLogger(NanoleafPanelColors.class);
+
+ // holds current color data per panel
+ private final Map<Integer, HSBType> panelColors = new ConcurrentHashMap<>();
+ private final Map<Integer, NanoleafPanelColorChangeListener> panelChangeListeners = new ConcurrentHashMap<>();
+ private @Nullable NanoleafControllerColorChangeListener controllerListener;
+
+ private boolean updatePanelColorNoController(Integer panelId, HSBType color) {
+ boolean updatePanel = false;
+ if (panelColors.containsKey(panelId)) {
+ HSBType existingColor = panelColors.get(panelId);
+ if (existingColor != null && !existingColor.equals(color)) {
+ // Color change - update the panel thing
+ updatePanel = true;
+ }
+ } else {
+ // First time we see this panels color - update the panel thing
+ updatePanel = true;
+ }
+
+ panelColors.put(panelId, color);
+
+ if (updatePanel) {
+ @Nullable
+ NanoleafPanelColorChangeListener panelHandler = panelChangeListeners.get(panelId);
+ if (panelHandler != null) {
+ panelHandler.onPanelChangedColor(color);
+ }
+ }
+
+ return updatePanel;
+ }
+
+ private void updatePanelColor(Integer panelId, HSBType color) {
+ boolean updatePanel = updatePanelColorNoController(panelId, color);
+ if (updatePanel) {
+ notifyControllerListener();
+ }
+ }
+
+ private void notifyControllerListener() {
+ NanoleafControllerColorChangeListener privateControllerListener = controllerListener;
+ if (privateControllerListener != null) {
+ privateControllerListener.onPanelChangedColor();
+ }
+ }
+
+ /**
+ * Retrieves the color of the panel. Used by the panels to read their state.
+ *
+ * @param panelId The id of the panel
+ * @return The color of the panel
+ */
+ public @Nullable HSBType getPanelColor(Integer panelId) {
+ return panelColors.get(panelId);
+ }
+
+ /**
+ * Called from panels to update the state.
+ *
+ * @param panelId The panel that received the update
+ * @param color The new color of the panel
+ */
+ public void setPanelColor(Integer panelId, HSBType color) {
+ updatePanelColor(panelId, color);
+ }
+
+ public void registerChangeListener(Integer panelId, NanoleafPanelHandler panelListener) {
+ logger.trace("Adding color change listener for panel {}", panelId);
+ panelChangeListeners.put(panelId, panelListener);
+ }
+
+ public void unregisterChangeListener(Integer panelId) {
+ logger.trace("Removing color change listener for panel {}", panelId);
+ panelChangeListeners.remove(panelId);
+ }
+
+ public void registerChangeListener(NanoleafControllerColorChangeListener controllerListener) {
+ logger.trace("Setting color change listener for controller");
+ this.controllerListener = controllerListener;
+ }
+
+ /**
+ * Returns the color of a panel.
+ *
+ * @param panelId The panel
+ * @param defaultColor Default color if panel is missing color information
+ * @return Color of the panel
+ */
+ public HSBType getColor(Integer panelId, HSBType defaultColor) {
+ return panelColors.getOrDefault(panelId, defaultColor);
+ }
+
+ /**
+ * Returns true if we have color information for the given panel.
+ *
+ * @param panelId The panel to check if has color
+ * @return true if we have color information about the panel
+ */
+ public boolean hasColor(Integer panelId) {
+ return panelColors.containsKey(panelId);
+ }
+
+ /**
+ * Sets all panels to the same color. This will make controller repaint only once.
+ *
+ * @param panelIds Panels to update
+ * @param color The color for all panels
+ */
+ public void setMultiple(List<Integer> panelIds, HSBType color) {
+ logger.debug("Setting all panels to color {}", color);
+ boolean updatePanel = false;
+ for (Integer panelId : panelIds) {
+ updatePanel |= updatePanelColorNoController(panelId, color);
+ }
+
+ if (updatePanel) {
+ notifyControllerListener();
+ }
+ }
+}
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.openhab.binding.nanoleaf.internal.NanoleafBadRequestException;
import org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants;
import org.openhab.binding.nanoleaf.internal.NanoleafControllerListener;
import org.openhab.binding.nanoleaf.internal.NanoleafException;
+import org.openhab.binding.nanoleaf.internal.NanoleafNotFoundException;
import org.openhab.binding.nanoleaf.internal.NanoleafUnauthorizedException;
import org.openhab.binding.nanoleaf.internal.OpenAPIUtils;
+import org.openhab.binding.nanoleaf.internal.colors.NanoleafControllerColorChangeListener;
+import org.openhab.binding.nanoleaf.internal.colors.NanoleafPanelColors;
import org.openhab.binding.nanoleaf.internal.commanddescription.NanoleafCommandDescriptionProvider;
import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig;
import org.openhab.binding.nanoleaf.internal.discovery.NanoleafPanelsDiscoveryService;
+import org.openhab.binding.nanoleaf.internal.layout.ConstantPanelState;
import org.openhab.binding.nanoleaf.internal.layout.LayoutSettings;
+import org.openhab.binding.nanoleaf.internal.layout.LivePanelState;
import org.openhab.binding.nanoleaf.internal.layout.NanoleafLayout;
import org.openhab.binding.nanoleaf.internal.layout.PanelState;
import org.openhab.binding.nanoleaf.internal.model.AuthToken;
import org.openhab.binding.nanoleaf.internal.model.Layout;
import org.openhab.binding.nanoleaf.internal.model.On;
import org.openhab.binding.nanoleaf.internal.model.PanelLayout;
+import org.openhab.binding.nanoleaf.internal.model.PositionDatum;
import org.openhab.binding.nanoleaf.internal.model.Rhythm;
import org.openhab.binding.nanoleaf.internal.model.Sat;
import org.openhab.binding.nanoleaf.internal.model.State;
import org.openhab.binding.nanoleaf.internal.model.TouchEvents;
+import org.openhab.binding.nanoleaf.internal.model.Write;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.library.types.DecimalType;
* @author Kai Kreuzer - refactoring, bug fixing and code clean up
*/
@NonNullByDefault
-public class NanoleafControllerHandler extends BaseBridgeHandler {
+public class NanoleafControllerHandler extends BaseBridgeHandler implements NanoleafControllerColorChangeListener {
// Pairing interval in seconds
private static final int PAIRING_INTERVAL = 10;
private @Nullable Request sseTouchjobRequest;
private final List<NanoleafControllerListener> controllerListeners = new CopyOnWriteArrayList<NanoleafControllerListener>();
private PanelLayout previousPanelLayout = new PanelLayout();
+ private final NanoleafPanelColors panelColors = new NanoleafPanelColors();
private @NonNullByDefault({}) ScheduledFuture<?> pairingJob;
private @NonNullByDefault({}) ScheduledFuture<?> updateJob;
@Override
public void initialize() {
logger.debug("Initializing the controller (bridge)");
+ this.panelColors.registerChangeListener(this);
updateStatus(ThingStatus.UNKNOWN);
NanoleafControllerConfig config = getConfigAs(NanoleafControllerConfig.class);
setAddress(config.address);
if (panelHandler != null) {
logger.trace("Checking available panel -{}- versus event panel -{}-", panelHandler.getPanelID(),
event.getPanelId());
- if (panelHandler.getPanelID().equals(event.getPanelId())) {
+ if (panelHandler.getPanelID().equals(Integer.valueOf(event.getPanelId()))) {
logger.debug("Panel {} found. Triggering item with gesture {}.", panelHandler.getPanelID(),
event.getGesture());
panelHandler.updatePanelGesture(event.getGesture());
Brightness stateBrightness = state.getBrightness();
int brightness = stateBrightness != null ? stateBrightness.getValue() : 0;
+ HSBType stateColor = new HSBType(new DecimalType(hue), new PercentType(saturation),
+ new PercentType(powerState == OnOffType.ON ? brightness : 0));
- updateState(CHANNEL_COLOR, new HSBType(new DecimalType(hue), new PercentType(saturation),
- new PercentType(powerState == OnOffType.ON ? brightness : 0)));
+ updateState(CHANNEL_COLOR, stateColor);
updateState(CHANNEL_COLOR_MODE, new StringType(state.getColorMode()));
updateState(CHANNEL_RHYTHM_ACTIVE, controllerInfo.getRhythm().getRhythmActive() ? OnOffType.ON : OnOffType.OFF);
updateState(CHANNEL_RHYTHM_MODE, new DecimalType(controllerInfo.getRhythm().getRhythmMode()));
updateState(CHANNEL_RHYTHM_STATE,
controllerInfo.getRhythm().getRhythmConnected() ? OnOffType.ON : OnOffType.OFF);
- // update the color channels of each panel
- getThing().getThings().forEach(child -> {
- NanoleafPanelHandler panelHandler = (NanoleafPanelHandler) child.getHandler();
- if (panelHandler != null) {
- logger.debug("Update color channel for panel {}", panelHandler.getThing().getUID());
- panelHandler.updatePanelColorChannel();
- }
- });
-
+ updatePanelColors();
+ if (EFFECT_NAME_SOLID_COLOR.equals(controllerInfo.getEffects().getSelect())) {
+ setSolidColor(stateColor);
+ }
updateProperties();
updateConfiguration();
updateLayout(controllerInfo.getPanelLayout());
- updateVisualState(controllerInfo.getPanelLayout());
+ updateVisualState(controllerInfo.getPanelLayout(), powerState);
for (NanoleafControllerListener controllerListener : controllerListeners) {
controllerListener.onControllerInfoFetched(getThing().getUID(), controllerInfo);
}
}
+ private void setSolidColor(HSBType color) {
+ // If the panels are set to solid color, they are read from the state
+ PanelLayout panelLayout = controllerInfo.getPanelLayout();
+ Layout layout = panelLayout.getLayout();
+
+ if (layout != null) {
+ List<PositionDatum> positionData = layout.getPositionData();
+ if (positionData != null) {
+ List<Integer> allPanelIds = new ArrayList<>(positionData.size());
+ for (PositionDatum pd : positionData) {
+ allPanelIds.add(pd.getPanelId());
+ }
+
+ panelColors.setMultiple(allPanelIds, color);
+ } else {
+ logger.debug("Missing position datum when setting solid color for {}", getThing().getUID());
+ }
+ } else {
+ logger.debug("Missing layout when setting solid color for {}", getThing().getUID());
+ }
+ }
+
private void updateConfiguration() {
// only update the Thing config if value isn't set yet
if (getConfig().get(NanoleafControllerConfig.DEVICE_TYPE) == null) {
}
}
- private void updateVisualState(PanelLayout panelLayout) {
+ private void updateVisualState(PanelLayout panelLayout, OnOffType powerState) {
ChannelUID stateChannel = new ChannelUID(getThing().getUID(), CHANNEL_VISUAL_STATE);
- Bridge bridge = getThing();
- List<Thing> things = bridge.getThings();
- if (things == null) {
- logger.trace("No things to get state from!");
- return;
- }
-
try {
+ PanelState panelState;
+ if (OnOffType.OFF.equals(powerState)) {
+ // If powered off: show all panels as black
+ panelState = new ConstantPanelState(HSBType.BLACK);
+ } else {
+ // Static color for panels, use it
+ panelState = new LivePanelState(panelColors);
+ }
+
LayoutSettings settings = new LayoutSettings(false, true, true, true);
- logger.trace("Getting panel state for {} things", things.size());
- PanelState panelState = new PanelState(things);
byte[] bytes = NanoleafLayout.render(panelLayout, panelState, settings);
if (bytes.length > 0) {
updateState(stateChannel, new RawType(bytes, "image/png"));
return;
}
- Bridge bridge = getThing();
- List<Thing> things = bridge.getThings();
try {
LayoutSettings settings = new LayoutSettings(true, false, true, false);
- byte[] bytes = NanoleafLayout.render(panelLayout, new PanelState(things), settings);
+ byte[] bytes = NanoleafLayout.render(panelLayout, new LivePanelState(panelColors), settings);
if (bytes.length > 0) {
updateState(layoutChannel, new RawType(bytes, "image/png"));
logger.trace("Rendered layout of panel {} in updateState has {} bytes", getThing().getUID(),
h.setValue(((HSBType) command).getHue().intValue());
s.setValue(((HSBType) command).getSaturation().intValue());
b.setValue(((HSBType) command).getBrightness().intValue());
+ setSolidColor((HSBType) command);
stateObject.setState(h);
stateObject.setState(s);
stateObject.setState(b);
}
}
+ private boolean hasStaticEffect() {
+ return EFFECT_NAME_STATIC_COLOR.equals(controllerInfo.getEffects().getSelect())
+ || EFFECT_NAME_SOLID_COLOR.equals(controllerInfo.getEffects().getSelect());
+ }
+
+ /**
+ * Checks if we are in a mode where color changes should be rendered.
+ *
+ * @return True if a color change on a panel should be rendered
+ */
+ private boolean showsUpdatedColors() {
+ if (!hasStaticEffect()) {
+ return false;
+ }
+
+ State state = controllerInfo.getState();
+ OnOffType powerState = state.getOnOff();
+ return OnOffType.ON.equals(powerState);
+ }
+
+ @Override
+ public void onPanelChangedColor() {
+ if (showsUpdatedColors()) {
+ // Update the visual state if a panel has changed color
+ updateVisualState(controllerInfo.getPanelLayout(), controllerInfo.getState().getOnOff());
+ }
+ }
+
+ /**
+ * For individual panels to get access to the panel colors.
+ *
+ * @return Information about colors of panels.
+ */
+ public NanoleafPanelColors getColorInformation() {
+ return panelColors;
+ }
+
+ private void updatePanelColors() {
+ // get panel color data from controller
+ try {
+ Effects effects = new Effects();
+ Write write = new Write();
+ write.setCommand("request");
+ write.setAnimName(EFFECT_NAME_STATIC_COLOR);
+ effects.setWrite(write);
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ NanoleafControllerHandler handler = (NanoleafControllerHandler) bridge.getHandler();
+ if (handler != null) {
+ NanoleafControllerConfig config = handler.getControllerConfig();
+ logger.debug("Sending Request from Panel for getColor()");
+ Request setPanelUpdateRequest = OpenAPIUtils.requestBuilder(httpClient, config, API_EFFECT,
+ HttpMethod.PUT);
+ setPanelUpdateRequest.content(new StringContentProvider(gson.toJson(effects)), "application/json");
+ ContentResponse panelData = OpenAPIUtils.sendOpenAPIRequest(setPanelUpdateRequest);
+ // parse panel data
+
+ parsePanelData(config, panelData);
+ }
+ }
+ } catch (NanoleafNotFoundException nfe) {
+ logger.debug("Panel data could not be retrieved as no data was returned (static type missing?) : {}",
+ nfe.getMessage());
+ } catch (NanoleafBadRequestException nfe) {
+ logger.debug(
+ "Panel data could not be retrieved as request not expected(static type missing / dynamic type on) : {}",
+ nfe.getMessage());
+ } catch (NanoleafException nue) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "@text/error.nanoleaf.panel.communication");
+ logger.debug("Panel data could not be retrieved: {}", nue.getMessage());
+ }
+ }
+
+ void parsePanelData(NanoleafControllerConfig config, ContentResponse panelData) {
+ // panelData is in format (numPanels, (PanelId, 1, R, G, B, W, TransitionTime) * numPanel)
+ @Nullable
+ Write response = null;
+
+ String panelDataContent = panelData.getContentAsString();
+ try {
+ response = gson.fromJson(panelDataContent, Write.class);
+ } catch (JsonSyntaxException jse) {
+ logger.warn("Unable to parse panel data information from Nanoleaf", jse);
+ logger.trace("Panel Data which couldn't be parsed: {}", panelDataContent);
+ }
+
+ if (response != null) {
+ String[] tokenizedData = response.getAnimData().split(" ");
+ if (config.deviceType.equals(CONFIG_DEVICE_TYPE_LIGHTPANELS)
+ || config.deviceType.equals(CONFIG_DEVICE_TYPE_CANVAS)) {
+ // panelData is in format (numPanels (PanelId 1 R G B W TransitionTime) * numPanel)
+ String[] panelDataPoints = Arrays.copyOfRange(tokenizedData, 1, tokenizedData.length);
+ for (int i = 0; i < panelDataPoints.length; i++) {
+ if (i % 7 == 0) {
+ // found panel data - store it
+ panelColors.setPanelColor(Integer.valueOf(panelDataPoints[i]),
+ HSBType.fromRGB(Integer.parseInt(panelDataPoints[i + 2]),
+ Integer.parseInt(panelDataPoints[i + 3]),
+ Integer.parseInt(panelDataPoints[i + 4])));
+ }
+ }
+ } else {
+ // panelData is in format (0 numPanels (quotient(panelID) remainder(panelID) R G B W 0
+ // quotient(TransitionTime) remainder(TransitionTime)) * numPanel)
+ String[] panelDataPoints = Arrays.copyOfRange(tokenizedData, 2, tokenizedData.length);
+ for (int i = 0; i < panelDataPoints.length; i++) {
+ if (i % 8 == 0) {
+ Integer idQuotient = Integer.valueOf(panelDataPoints[i]);
+ Integer idRemainder = Integer.valueOf(panelDataPoints[i + 1]);
+ Integer idNum = idQuotient * 256 + idRemainder;
+ // found panel data - store it
+ panelColors.setPanelColor(idNum, HSBType.fromRGB(Integer.parseInt(panelDataPoints[i + 3]),
+ Integer.parseInt(panelDataPoints[i + 4]), Integer.parseInt(panelDataPoints[i + 5])));
+ }
+ }
+ }
+ }
+ }
+
private @Nullable String getAddress() {
return address;
}
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
-import org.openhab.binding.nanoleaf.internal.NanoleafBadRequestException;
import org.openhab.binding.nanoleaf.internal.NanoleafException;
-import org.openhab.binding.nanoleaf.internal.NanoleafNotFoundException;
import org.openhab.binding.nanoleaf.internal.NanoleafUnauthorizedException;
import org.openhab.binding.nanoleaf.internal.OpenAPIUtils;
+import org.openhab.binding.nanoleaf.internal.colors.NanoleafPanelColorChangeListener;
import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig;
import org.openhab.binding.nanoleaf.internal.model.Effects;
import org.openhab.binding.nanoleaf.internal.model.Write;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
+import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
* @author Stefan Höhn - Canvas Touch Support
*/
@NonNullByDefault
-public class NanoleafPanelHandler extends BaseThingHandler {
+public class NanoleafPanelHandler extends BaseThingHandler implements NanoleafPanelColorChangeListener {
private static final PercentType MIN_PANEL_BRIGHTNESS = PercentType.ZERO;
private static final PercentType MAX_PANEL_BRIGHTNESS = PercentType.HUNDRED;
private final HttpClient httpClient;
// JSON parser for API responses
private final Gson gson = new Gson();
-
- // holds current color data per panel
- private final Map<String, HSBType> panelInfo = new HashMap<>();
+ private HSBType currentPanelColor = HSBType.BLACK;
private @NonNullByDefault({}) ScheduledFuture<?> singleTapJob;
private @NonNullByDefault({}) ScheduledFuture<?> doubleTapJob;
@Override
public void handleRemoval() {
logger.debug("Nanoleaf panel {} removed", getThing().getUID());
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ ThingHandler handler = bridge.getHandler();
+ if (handler instanceof NanoleafControllerHandler) {
+ ((NanoleafControllerHandler) handler).getColorInformation().unregisterChangeListener(getPanelID());
+ }
+ }
+
super.handleRemoval();
}
private void initializePanel(ThingStatusInfo panelStatus) {
updateStatus(panelStatus.getStatus(), panelStatus.getStatusDetail());
+ updateState(CHANNEL_PANEL_COLOR, currentPanelColor);
logger.debug("Panel {} status changed to {}-{}", this.getThing().getUID(), panelStatus.getStatus(),
panelStatus.getStatusDetail());
+
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ ThingHandler handler = bridge.getHandler();
+ if (handler instanceof NanoleafControllerHandler) {
+ ((NanoleafControllerHandler) handler).getColorInformation().registerChangeListener(getPanelID(), this);
+ }
+ }
}
private void sendRenderedEffectCommand(Command command) throws NanoleafException {
logger.debug("Command Type: {}", command.getClass());
- HSBType currentPanelColor = getPanelColor();
- if (currentPanelColor != null) {
- logger.debug("currentPanelColor: {}", currentPanelColor.toString());
- }
- HSBType newPanelColor = new HSBType();
+ logger.debug("currentPanelColor: {}", currentPanelColor);
+ HSBType newPanelColor = new HSBType();
if (command instanceof HSBType) {
newPanelColor = (HSBType) command;
- } else if (command instanceof OnOffType && (currentPanelColor != null)) {
+ } else if (command instanceof OnOffType) {
if (OnOffType.ON.equals(command)) {
newPanelColor = new HSBType(currentPanelColor.getHue(), currentPanelColor.getSaturation(),
MAX_PANEL_BRIGHTNESS);
newPanelColor = new HSBType(currentPanelColor.getHue(), currentPanelColor.getSaturation(),
MIN_PANEL_BRIGHTNESS);
}
- } else if (command instanceof PercentType && (currentPanelColor != null)) {
+ } else if (command instanceof PercentType) {
PercentType brightness = new PercentType(
Math.max(MIN_PANEL_BRIGHTNESS.intValue(), ((PercentType) command).intValue()));
newPanelColor = new HSBType(currentPanelColor.getHue(), currentPanelColor.getSaturation(), brightness);
- } else if (command instanceof IncreaseDecreaseType && (currentPanelColor != null)) {
+ } else if (command instanceof IncreaseDecreaseType) {
int brightness = currentPanelColor.getBrightness().intValue();
if (command.equals(IncreaseDecreaseType.INCREASE)) {
brightness = Math.min(MAX_PANEL_BRIGHTNESS.intValue(), brightness + BRIGHTNESS_STEP_SIZE);
return;
}
// store panel's new HSB value
- logger.trace("Setting new color {}", newPanelColor);
- panelInfo.put(getThing().getConfiguration().get(CONFIG_PANEL_ID).toString(), newPanelColor);
+ logger.trace("Setting new color {} to panel {}", newPanelColor, getPanelID());
+ setPanelColor(newPanelColor);
// transform to RGB
PercentType[] rgbPercent = newPanelColor.toRGB();
logger.trace("Setting new rgbpercent {} {} {}", rgbPercent[0], rgbPercent[1], rgbPercent[2]);
}
}
- public void updatePanelColorChannel() {
- @Nullable
- HSBType panelColor = getPanelColor();
- logger.trace("updatePanelColorChannel: panelColor: {}", panelColor);
- if (panelColor != null) {
- updateState(CHANNEL_PANEL_COLOR, panelColor);
- }
- }
-
/**
* Apply the gesture to the panel
*
}
}
- public String getPanelID() {
- String panelID = getThing().getConfiguration().get(CONFIG_PANEL_ID).toString();
- return panelID;
- }
-
- public @Nullable HSBType getColor() {
- String panelID = getPanelID();
- return panelInfo.get(panelID);
+ public Integer getPanelID() {
+ return (Integer) getThing().getConfiguration().get(CONFIG_PANEL_ID);
}
- private @Nullable HSBType getPanelColor() {
- String panelID = getPanelID();
-
- // get panel color data from controller
- try {
- Effects effects = new Effects();
- Write write = new Write();
- write.setCommand("request");
- write.setAnimName("*Static*");
- effects.setWrite(write);
- Bridge bridge = getBridge();
- if (bridge != null) {
- NanoleafControllerHandler handler = (NanoleafControllerHandler) bridge.getHandler();
- if (handler != null) {
- NanoleafControllerConfig config = handler.getControllerConfig();
- logger.debug("Sending Request from Panel for getColor()");
- Request setPanelUpdateRequest = OpenAPIUtils.requestBuilder(httpClient, config, API_EFFECT,
- HttpMethod.PUT);
- setPanelUpdateRequest.content(new StringContentProvider(gson.toJson(effects)), "application/json");
- ContentResponse panelData = OpenAPIUtils.sendOpenAPIRequest(setPanelUpdateRequest);
- // parse panel data
-
- parsePanelData(panelID, config, panelData);
- }
+ private void setPanelColor(HSBType color) {
+ Integer panelId = getPanelID();
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ ThingHandler handler = bridge.getHandler();
+ if (handler instanceof NanoleafControllerHandler) {
+ ((NanoleafControllerHandler) handler).getColorInformation().setPanelColor(panelId, color);
+ } else {
+ logger.debug("Couldn't find handler for panel {}", panelId);
}
- } catch (NanoleafNotFoundException nfe) {
- logger.debug("Panel data could not be retrieved as no data was returned (static type missing?) : {}",
- nfe.getMessage());
- } catch (NanoleafBadRequestException nfe) {
- logger.debug(
- "Panel data could not be retrieved as request not expected(static type missing / dynamic type on) : {}",
- nfe.getMessage());
- } catch (NanoleafException nue) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- "@text/error.nanoleaf.panel.communication");
- logger.debug("Panel data could not be retrieved: {}", nue.getMessage());
+ } else {
+ logger.debug("Couldn't find bridge for panel {}", panelId);
}
-
- return panelInfo.get(panelID);
}
- void parsePanelData(String panelID, NanoleafControllerConfig config, ContentResponse panelData) {
- // panelData is in format (numPanels, (PanelId, 1, R, G, B, W, TransitionTime) * numPanel)
- @Nullable
- Write response = gson.fromJson(panelData.getContentAsString(), Write.class);
- if (response != null) {
- String[] tokenizedData = response.getAnimData().split(" ");
- if (config.deviceType.equals(CONFIG_DEVICE_TYPE_LIGHTPANELS)
- || config.deviceType.equals(CONFIG_DEVICE_TYPE_CANVAS)) {
- // panelData is in format (numPanels (PanelId 1 R G B W TransitionTime) * numPanel)
- String[] panelDataPoints = Arrays.copyOfRange(tokenizedData, 1, tokenizedData.length);
- for (int i = 0; i < panelDataPoints.length; i++) {
- if (i % 7 == 0) {
- String id = panelDataPoints[i];
- if (id.equals(panelID)) {
- // found panel data - store it
- panelInfo.put(panelID,
- HSBType.fromRGB(Integer.parseInt(panelDataPoints[i + 2]),
- Integer.parseInt(panelDataPoints[i + 3]),
- Integer.parseInt(panelDataPoints[i + 4])));
- }
- }
- }
- } else {
- // panelData is in format (0 numPanels (quotient(panelID) remainder(panelID) R G B W 0
- // quotient(TransitionTime) remainder(TransitionTime)) * numPanel)
- String[] panelDataPoints = Arrays.copyOfRange(tokenizedData, 2, tokenizedData.length);
- for (int i = 0; i < panelDataPoints.length; i++) {
- if (i % 8 == 0) {
- Integer idQuotient = Integer.valueOf(panelDataPoints[i]);
- Integer idRemainder = Integer.valueOf(panelDataPoints[i + 1]);
- Integer idNum = idQuotient * 256 + idRemainder;
- if (String.valueOf(idNum).equals(panelID)) {
- // found panel data - store it
- panelInfo.put(panelID,
- HSBType.fromRGB(Integer.parseInt(panelDataPoints[i + 3]),
- Integer.parseInt(panelDataPoints[i + 4]),
- Integer.parseInt(panelDataPoints[i + 5])));
- }
- }
- }
- }
+ @Override
+ public void onPanelChangedColor(HSBType newColor) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("updatePanelColorChannel: panelColor: {} for panel {}", newColor, getPanelID());
}
+
+ currentPanelColor = newColor;
+ updateState(CHANNEL_PANEL_COLOR, newColor);
}
}
--- /dev/null
+/**
+ * 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.nanoleaf.internal.layout;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.HSBType;
+
+/**
+ * Always returns the same color for all panels.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+@NonNullByDefault
+public class ConstantPanelState implements PanelState {
+ private final HSBType color;
+
+ public ConstantPanelState(HSBType color) {
+ this.color = color;
+ }
+
+ @Override
+ public HSBType getHSBForPanel(Integer panelId) {
+ return color;
+ }
+}
--- /dev/null
+/**
+ * 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.nanoleaf.internal.layout;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.nanoleaf.internal.colors.NanoleafPanelColors;
+import org.openhab.core.library.types.HSBType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Stores the state of the panels.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+@NonNullByDefault
+public class LivePanelState implements PanelState {
+
+ private static final Logger logger = LoggerFactory.getLogger(LivePanelState.class);
+ private final NanoleafPanelColors panelColors;
+
+ public LivePanelState(NanoleafPanelColors panelColors) {
+ this.panelColors = panelColors;
+ }
+
+ @Override
+ public HSBType getHSBForPanel(Integer panelId) {
+ if (logger.isTraceEnabled()) {
+ if (!panelColors.hasColor(panelId)) {
+ logger.trace("Failed to get color for panel {}, falling back to black", panelId);
+ }
+ }
+
+ return panelColors.getColor(panelId, HSBType.BLACK);
+ }
+}
*
* SPDX-License-Identifier: EPL-2.0
*/
-
package org.openhab.binding.nanoleaf.internal.layout;
-import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.CONFIG_PANEL_ID;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.nanoleaf.internal.handler.NanoleafPanelHandler;
import org.openhab.core.library.types.HSBType;
-import org.openhab.core.thing.Thing;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Stores the state of the panels.
* @author Jørgen Austvik - Initial contribution
*/
@NonNullByDefault
-public class PanelState {
-
- private static final Logger logger = LoggerFactory.getLogger(PanelState.class);
- private final Map<Integer, HSBType> panelStates = new HashMap<>();
-
- public PanelState(List<Thing> panels) {
- for (Thing panel : panels) {
- Integer panelId = Integer.valueOf(panel.getConfiguration().get(CONFIG_PANEL_ID).toString());
- NanoleafPanelHandler panelHandler = (NanoleafPanelHandler) panel.getHandler();
- if (panelHandler != null) {
- HSBType c = panelHandler.getColor();
-
- if (c == null) {
- logger.trace("Panel {}: Failed to get color", panelId);
- }
-
- HSBType color = (c == null) ? HSBType.BLACK : c;
- panelStates.put(panelId, color);
- } else {
- logger.trace("Panel {}: Couldn't find handler", panelId);
- }
- }
- }
-
- public HSBType getHSBForPanel(Integer panelId) {
- if (logger.isTraceEnabled()) {
- if (!panelStates.containsKey(panelId)) {
- logger.trace("Failed to get color for panel {}, falling back to black", panelId);
- }
- }
+public interface PanelState {
- return panelStates.getOrDefault(panelId, HSBType.BLACK);
- }
+ HSBType getHSBForPanel(Integer panelId);
}
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
import org.eclipse.jdt.annotation.NonNull;
List<Panel> result = new ArrayList<>(panels.size());
Deque<PositionDatum> panelStack = new ArrayDeque<>(panels);
while (!panelStack.isEmpty()) {
- PositionDatum panel = panelStack.peek();
+ PositionDatum panel = Objects.requireNonNull(panelStack.peek());
final ShapeType shapeType = ShapeType.valueOf(panel.getShapeType());
Panel shape = createPanel(shapeType, takeFirst(shapeType.getNumLightsPerShape(), panelStack));
result.add(shape);
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collections;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
// Files.write(permanentOutFile, result);
}
- private class TestPanelState extends PanelState {
+ private class TestPanelState implements PanelState {
private final HSBType testColors[] = { HSBType.fromRGB(160, 120, 40), HSBType.fromRGB(80, 60, 20),
HSBType.fromRGB(120, 90, 30), HSBType.fromRGB(200, 150, 60) };
- public TestPanelState() {
- super(Collections.emptyList());
- }
-
@Override
public HSBType getHSBForPanel(Integer panelId) {
return testColors[panelId % testColors.length];