/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.hue.internal.hardware; import org.openhab.binding.hue.internal.data.HueSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; /** * The representation of a physical Hue bulb, providing control of the bulbs * features. * * @author Roman Hartmann * @author Kai Kreuzer * @author Jos Schering * @author Markus Mazurczak - Added workaround code to correctly manage On/Off switching of Osram Par16 50 TW bulbs * @since 1.2.0 * */ public class HueBulb { static final Logger logger = LoggerFactory.getLogger(HueBulb.class); /** * Enumeration of RGB color components that may be customized for the bulb. */ public enum ColorComponent { red, green, blue } private HueBridge bridge = null; private String deviceId; private boolean isOn = false; private boolean isReachable = false; private int brightness = 0; // possible values are 0 - 254 private int colorTemperature = 154; // possible values are 154 - 500 private int hue = 0; // 0 - 65535 private int saturation = 0; // 0 - 254 private boolean isOsramPar16 = false; // to indicate if the bulb must use the workaround code for on/off switching // name of the model ID for which the workaround code will be executed private static final String OSRAM_PAR16_MODELID = "PAR16 50 TW"; /** The maximum brightness value of the Hue bulb */ public static final int MAX_BRIGHTNESS = 254; /** The maximum saturation value of the Hue bulb */ public static final int MAX_SATURATION = 254; private Client client; public HueBulb(HueBridge connectedBridge, String deviceId) { this(connectedBridge, deviceId, connectedBridge.getSettings()); } /** * Constructor for the HueBulb. * * @param connectedBridge * The bridge the bulb is connected to. * @param deviceNumber * The number under which the bulb is filed in the bridge. */ public HueBulb(HueBridge connectedBridge, String deviceId, HueSettings settings) { this.bridge = connectedBridge; this.deviceId = deviceId; getStatus(settings); this.client = Client.create(); this.client.setReadTimeout(1000); this.client.setConnectTimeout(2000); } /** * Update the internal bulb status according to the Philips hub * * @param HueSettings retrieved from hub */ public void getStatus(HueSettings settings) { if (settings.isValidId(deviceId)) { this.isOn = settings.isBulbOn(this.deviceId); this.isReachable = settings.isReachable(this.deviceId); this.colorTemperature = settings.getColorTemperature(this.deviceId); this.brightness = settings.getBrightness(this.deviceId); this.hue = settings.getHue(this.deviceId); this.saturation = settings.getSaturation(this.deviceId); // set isOsram16 to true if the bulb is of that special type if (settings.getModelId(this.deviceId).equalsIgnoreCase(OSRAM_PAR16_MODELID)) { this.isOsramPar16 = true; } } else { logger.warn("Not a valid id on the bridge: " + deviceId); throw new IllegalStateException("Not a valid id on the bridge: " + deviceId); } } /** * Changes the color of the bulb to the color defined in the HSB format. * * @param newHue * The hue of the color. (0..1) * @param newSaturation * The saturation of the color. (0..1) * @param newBrightness * The brightness of the color. (0..1) */ public void colorizeByHSB(double newHue, double newSaturation, double newBrightness) { int newHueCalculated = new Long(Math.round(newHue * 360.0 * 182.0)).intValue(); int newSaturationCalculated = new Long(Math.round(newSaturation * MAX_SATURATION)).intValue(); int newBrightnessCalculated = new Long(Math.round(newBrightness * MAX_BRIGHTNESS)).intValue(); colorizeByHSBInternally(newHueCalculated, newSaturationCalculated, newBrightnessCalculated); } /** * Increases the brightness of the bulb by the given amount to a maximum of * {@link #MAX_BRIGHTNESS}. If the bulb is not switched on this will be done implicitly. * * @param amount * The amount by which the brightness shall be increased. * @return The resulting brightness in percent 0-100 */ public int increaseBrightness(int amount) { return setBrightness(this.brightness + amount); } /** * Decreases the brightness of the bulb by the given amount to a minimum of * 0. If the bulb reaches a brightness of 0 the bulb will be switched off. * * @param amount * The amount by which the brightness shall be decreased. * @return The resulting brightness in percent 0-100 */ public int decreaseBrightness(int amount) { return setBrightness(this.brightness - amount); } /** * Sets the brightness of the bulb to the given amount (0 - 255). If the * bulb has a brightness of 0 it will be switched off. * * @param brightness * The new brightness. * @return The resulting brightness in percent 0-100 */ public int setBrightness(int brightness) { this.brightness = brightness; this.brightness = this.brightness < 0 ? 0 : this.brightness; this.brightness = this.brightness > MAX_BRIGHTNESS ? MAX_BRIGHTNESS : this.brightness; if (this.brightness > 0) { this.isOn = true; executeMessage("{\"bri\":" + this.brightness + ",\"on\":true}"); } else { // Call the switchOn method to take care of Osram bulb while dimming this.switchOn(false); } return (int) Math.round((100.0 / MAX_BRIGHTNESS) * this.brightness); } /** * Set bulb ON/OFF without changing the brightness. * Takes care in case of an Osram Par 16 50 TW bulb. Some workaround message will be send to bridge * * @param on * true turn bulb on * false turn bulb off */ public boolean switchOn(boolean powerOn) { this.isOn = powerOn; if (powerOn) { if (this.isOsramPar16) { executeMessage("{\"on\":true,\"bri\":254}"); } else { executeMessage("{\"on\":true}"); } } else { if (this.isOsramPar16) { executeMessage("{\"on\":false,\"transitiontime\":0}"); } else { executeMessage("{\"on\":false}"); } } return true; } /** * Increases the color temperature of the bulb by the given amount to a * maximum of 500. * * @param amount * The amount by which the color brightness shall be increased. */ public void increaseColorTemperature(int amount) { setColorTemperature(this.colorTemperature + amount); } /** * Decreases the color temperature of the bulb by the given amount to a * minimum of 154. * * @param amount * The amount by which the color brightness shall be decreased. */ public void decreaseColorTemperature(int amount) { setColorTemperature(this.colorTemperature - amount); } /** * Sets the color temperature of the bulb to the given amount (154 - 500). * * @param temperature * The amount by which the color brightness shall be decreased. */ public void setColorTemperature(int temperature) { this.colorTemperature = temperature; this.colorTemperature = this.colorTemperature < 154 ? 154 : this.colorTemperature; this.colorTemperature = this.colorTemperature > 500 ? 500 : this.colorTemperature; executeMessage("{\"ct\":" + this.colorTemperature + "}"); } /** * Switches the bulb on or off at full brightness. * * @param newState * True if the bulb should be turned on at full brightness, false * to turn it off. */ public void setOnAtFullBrightness(boolean newState) { if (newState) { increaseBrightness(MAX_BRIGHTNESS); } else { decreaseBrightness(MAX_BRIGHTNESS); } } /** * Sends the values to the bulb to change its color accordingly. * * @param hue * The hue of the color. (0..65535) * @param saturation * The saturation of the color. (0..{@link #MAX_SATURATION}) * @param brightness * The brightness of the color. (0..{@link #MAX_BRIGHTNESS}) */ private void colorizeByHSBInternally(int hue, int saturation, int brightness) { this.hue = hue; this.saturation = saturation; this.brightness = brightness; this.isOn = true; String input = "{\"hue\":" + this.hue + ",\"sat\":" + this.saturation + ",\"bri\":" + this.brightness + ",\"on\":" + this.isOn + "}"; executeMessage(input); } /** * Sends the message to the bulb for execution. * * @param message * A Json message with the information that should be send to the * bulb. */ private void executeMessage(String message) { String targetURL = bridge.getUrl() + "lights/" + deviceId + "/state"; WebResource webResource = client.resource(targetURL); ClientResponse response = webResource.type("application/json").put(ClientResponse.class, message); logger.debug("Sent message: '" + message + "' to " + targetURL); if (response.getStatus() != 200) { logger.error("Failed to connect to Hue bridge: HTTP error code: " + response.getStatus()); } } /** * Return on / off status of bulb * * @return */ public boolean getIsOn() { return isOn; } /** * Return isReachable status of bulb * * @return */ public boolean getIsReachable() { return isReachable; } /** * Return Hue value of bulb * * @return */ public int getHue() { return hue; } /** * Return Saturation of bulb * * @return */ public int getSaturation() { return saturation; } /** * Return Brightness of bulb * * @return */ public int getBrightness() { return brightness; } }