diff --git a/pom.xml b/pom.xml --- a/pom.xml +++ b/pom.xml @@ -1,19 +1,19 @@ 4.0.0 org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.0.1 + 3.1.0 ../openhab-addons/bundles - 3.0.2-SNAPSHOT + 3.1.0 org.openhab.binding.lutronmqtt openHAB Add-ons :: Bundles :: Lutron-MQTT Binding diff --git a/src/main/history/dependencies.xml b/src/main/history/dependencies.xml --- a/src/main/history/dependencies.xml +++ b/src/main/history/dependencies.xml @@ -1,10 +1,10 @@ - + openhab-runtime-base openhab-transport-mqtt wrap - mvn:org.openhab.addons.bundles/org.openhab.binding.lutronmqtt/3.0.2-SNAPSHOT + mvn:org.openhab.addons.bundles/org.openhab.binding.lutronmqtt/3.1.0 wrap:mvn:org.lastnpe.eea/eea-all/2.2.1 diff --git a/src/main/java/org/openhab/binding/lutronmqtt/handler/LightStateConverter.java b/src/main/java/org/openhab/binding/lutronmqtt/handler/LightStateConverter.java --- a/src/main/java/org/openhab/binding/lutronmqtt/handler/LightStateConverter.java +++ b/src/main/java/org/openhab/binding/lutronmqtt/handler/LightStateConverter.java @@ -1,181 +1,193 @@ package org.openhab.binding.lutronmqtt.handler; import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.LUTRON_PROPERTY_LEVEL; import java.util.HashMap; import java.util.Map; import org.openhab.binding.lutronmqtt.model.LutronDevice; 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.UpDownType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The {@link LightStateConverter} is responsible for mapping Eclipse SmartHome * types to jue types and vice versa. * * @author Dennis Nobel - Initial contribution * @author Oliver Libutzki - Adjustments * @author Kai Kreuzer - made code static * @author Andre Fuechsel - added method for brightness * @author Yordan Zhelev - added method for alert * @author Denis Dudnik - switched to internally integrated source of Jue library, minor code cleanup * */ public class LightStateConverter { private static final double BRIGHTNESS_FACTOR = 1.0; private static final int DIM_STEPSIZE = 65535 / 25; // 4 % protected static Logger log = LoggerFactory.getLogger(LightStateConverter.class); /** * Transforms the given {@link OnOffType} into a light state containing the * 'on' value. * * @param onOffType * on or off state * @return light state containing the 'on' value */ - public static int toOnOffLightState(OnOffType onOffType) { + public static int toOnOffLightState(OnOffType onOffType, boolean inverted) { int f = 0; if (onOffType == OnOffType.ON) f = 65535; + if (inverted) + f = 65535 - f; return f; } - public static int toPercentLightState(PercentType percentType) { + public static int toPercentLightState(PercentType percentType, boolean inverted) { int f = 0; if (PercentType.ZERO.equals(percentType)) f = 0; else if (PercentType.HUNDRED.equals(percentType)) f = 65535; else { f = (int) Math.round(percentType.floatValue() * 65535 / 100); } + if (inverted) + f = 65535 - f; return f; } - public static Map toLightState(OnOffType onOffType, LutronDevice device) { - int level = toOnOffLightState(onOffType); + public static Map toLightState(OnOffType onOffType, LutronDevice device, boolean inverted) { + int level = toOnOffLightState(onOffType, inverted); Map m = makeGoToLevelCommand(level, device); return m; } public static Map makeGoToLevelCommand(int level, LutronDevice device) { Map a = new HashMap<>(); log.warn("device: " + device); a.put("ObjectId", device.getObjectId()); a.put("ObjectType", 15); a.put("Fade", 0); a.put("Delay", 0); a.put("Level", level); Map m = new HashMap<>(); m.put("cmd", "GoToLevel"); m.put("args", a); return m; } /** * Transforms the given {@link PercentType} into a light state containing * the brightness and the 'on' value represented by {@link PercentType}. * * @param percentType * brightness represented as {@link PercentType} * @return light state containing the brightness and the 'on' value */ - public static Map toLightState(PercentType percentType, LutronDevice device) { - int level = toPercentLightState(percentType); + public static Map toLightState(PercentType percentType, LutronDevice device, boolean inverted) { + int level = toPercentLightState(percentType, inverted); Map m = makeGoToLevelCommand(level, device); return m; } - public static Map toLightState(IncreaseDecreaseType increaseDecreaseType, LutronDevice device) { - int level = toAdjustedBrightness(increaseDecreaseType, device.getProperty(LUTRON_PROPERTY_LEVEL)); + public static Map toLightState(IncreaseDecreaseType increaseDecreaseType, LutronDevice device, + boolean inverted) { + int level = toAdjustedBrightness(increaseDecreaseType, device.getProperty(LUTRON_PROPERTY_LEVEL), inverted); Map m = makeGoToLevelCommand(level, device); return m; } - public static Map toLightState(UpDownType upDownType, LutronDevice device) { - int level = toAdjustedBrightness(upDownType, device.getProperty(LUTRON_PROPERTY_LEVEL)); + public static Map toLightState(UpDownType upDownType, LutronDevice device, boolean inverted) { + int level = toAdjustedBrightness(upDownType, device.getProperty(LUTRON_PROPERTY_LEVEL), inverted); Map m = makeGoToLevelCommand(level, device); return m; } /** * Adjusts the given brightness using the {@link IncreaseDecreaseType} and * returns the updated value. * * @param command * The {@link IncreaseDecreaseType} to be used * @param currentBrightness * The current brightness * @return The adjusted brightness value */ - public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) { + public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness, boolean inverted) { int newBrightness; if (command == IncreaseDecreaseType.DECREASE) { newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0); } else { newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, 65535); } + if (inverted) + newBrightness = 65535 - newBrightness; return newBrightness; } /** * Adjusts the given brightness using the {@link IncreaseDecreaseType} and * returns the updated value. * * @param command * The {@link UpDownType} to be used * @param currentBrightness * The current brightness * @return The adjusted brightness value */ - public static int toAdjustedBrightness(UpDownType command, int currentBrightness) { + public static int toAdjustedBrightness(UpDownType command, int currentBrightness, boolean inverted) { int newBrightness; if (command == UpDownType.DOWN) { newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0); } else { newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, 65535); } + + if (inverted) + newBrightness = 65535 - newBrightness; return newBrightness; } /** * Transforms Luton device state into {@link PercentType} representing * the brightness. * * @param device * lutron device * @return percent type representing the brightness */ - public static PercentType toBrightnessPercentType(LutronDevice device) { + public static PercentType toBrightnessPercentType(LutronDevice device, boolean inverted) { int percent = (int) Math.round(device.getProperty(LUTRON_PROPERTY_LEVEL) / (65535 / 100)); + if (inverted) + percent = 100 - percent; if (log.isTraceEnabled()) { log.trace("Converting " + device.getProperty(LUTRON_PROPERTY_LEVEL) + " -> " + percent + " -> " + new PercentType(restrictToBounds(percent))); } return new PercentType(restrictToBounds(percent)); } private static int restrictToBounds(int percentValue) { if (percentValue < 0) { return 0; } else if (percentValue > 100) { return 100; } return percentValue; } } diff --git a/src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTShadeHandler.java b/src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTShadeHandler.java --- a/src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTShadeHandler.java +++ b/src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTShadeHandler.java @@ -1,25 +1,26 @@ /** * Copyright (c) 2014-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.lutronmqtt.handler; import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.*; import org.openhab.core.thing.Thing; /** * The {@link LutronMQTTShadeHandler} is responsible for handling commands, which are * sent to one of the channels. * * @author William Welliver - Initial contribution */ public class LutronMQTTShadeHandler extends PowerLevelDeviceHandler { public LutronMQTTShadeHandler(Thing thing) { super(thing, CHANNEL_SHADE_LEVEL, CHANNEL_POWER_SWITCH); + inverted = true; } } diff --git a/src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java b/src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java --- a/src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java +++ b/src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java @@ -1,279 +1,282 @@ /** * Copyright (c) 2014-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.lutronmqtt.handler; import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.*; import java.util.Map; import org.openhab.binding.lutronmqtt.model.LutronDevice; import org.openhab.core.library.types.*; import org.openhab.core.thing.*; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; /** * The {@link PowerLevelDeviceHandler} is responsible for handling commands, which are * sent to one of the channels. * * @author William Welliver - Initial contribution */ public class PowerLevelDeviceHandler extends BaseThingHandler implements DeviceStatusListener { protected Logger log = LoggerFactory.getLogger(getClass()); protected int objectId; // protected int deviceId; // // protected int integrationId; + + protected boolean inverted = false; + protected LutronDevice device; // last update received for this device. // protected int linkAddress; boolean setAsIs = false; protected LutronMQTTHubHandler hubHandler; protected final String powerLevelChannelName; // protected final String powerSwitchChannelName; public PowerLevelDeviceHandler(Thing thing, String powerLevelChannel, String powerSwitchChannel) { super(thing); this.powerLevelChannelName = powerLevelChannel; // this.powerSwitchChannelName = powerSwitchChannel; } @Override public void handleCommand(ChannelUID channelUID, Command command) { Map lightState = null; String ch = channelUID.getId(); log.warn("Got a command for channel id=" + ch + ", command=" + command); log.info("command= " + command + ", last device reading=" + getDevice().getProperty(LUTRON_PROPERTY_LEVEL)); if (log.isTraceEnabled()) { log.trace( "command= " + command + ", last device reading=" + getDevice().getProperty(LUTRON_PROPERTY_LEVEL)); } if (powerLevelChannelName.equals(ch)) { if (command instanceof PercentType) { - lightState = LightStateConverter.toLightState((PercentType) command, getDevice()); + lightState = LightStateConverter.toLightState((PercentType) command, getDevice(), inverted); } else if (command instanceof OnOffType) { - lightState = LightStateConverter.toLightState((OnOffType) command, getDevice()); + lightState = LightStateConverter.toLightState((OnOffType) command, getDevice(), inverted); } else if (command instanceof IncreaseDecreaseType) { - lightState = LightStateConverter.toLightState((IncreaseDecreaseType) command, getDevice()); + lightState = LightStateConverter.toLightState((IncreaseDecreaseType) command, getDevice(), inverted); } else if (command instanceof UpDownType) { - lightState = LightStateConverter.toLightState((UpDownType) command, getDevice()); + lightState = LightStateConverter.toLightState((UpDownType) command, getDevice(), inverted); } else if (command == StopMoveType.STOP) { setAsIs = true; log.warn("STOPPING"); scheduleUpdateForDevice(objectId); // lightState = LightStateConverter.toLightState((Sto) command, getDevice()); } } /* * else if (powerSwitchChannelName.equals(ch)) { * if (command instanceof OnOffType) { * lightState = LightStateConverter.toOnOffLightState((OnOffType) command, getDevice()); * } * } */ Gson gson = new Gson(); log.info("converted " + command + " to " + gson.toJson(lightState)); if (lightState != null) { if (log.isTraceEnabled()) { // Gson gson = new Gson(); log.trace("converted " + command + " to " + gson.toJson(lightState)); } updateDeviceState(lightState); } else { log.warn("Got a command for an unhandled channel: " + ch); } } protected void updateDeviceState(Map lightState) { log.warn("updateDeviceState: " + lightState); getHubHandler().setDesiredState(objectId, lightState); } @Override public void initialize() { log.debug("Initializing power level device handler."); initializeThing((getBridge() == null) ? null : getBridge().getStatus()); } private void initializeThing(ThingStatus bridgeStatus) { log.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus); String objId = getThing().getProperties().get(PROPERTY_OBJECT_ID); final Integer _objectId; if (objId != null) _objectId = Integer.valueOf(objId); else _objectId = null; log.warn("intializeThing " + _objectId); if (_objectId != null) { this.objectId = _objectId; if (getHubHandler() != null) { if (bridgeStatus == ThingStatus.ONLINE) { getHubHandler().requestUpdateForDevice(objectId); LutronDevice device = getHubHandler().getDeviceByObjectId(objectId); if (device == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); return; } updateStatus(ThingStatus.ONLINE); // receiving a response to the request update method above should trigger a state change. // onDeviceStateChanged(getHubHandler().getDeviceByIntegrationId(integrationId)); // initializeProperties(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } } else { updateStatus(ThingStatus.OFFLINE); } } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); } } @Override public void dispose() { log.debug("Handler disposed. Unregistering listener."); if (objectId != 0) { // technically 0 is a valid device id but it appears to be reserved for the hub LutronMQTTHubHandler hubHandler = getHubHandler(); if (hubHandler != null) { hubHandler.unregisterDeviceStatusListener(this); this.hubHandler = null; } objectId = 0; } } protected synchronized LutronMQTTHubHandler getHubHandler() { if (this.hubHandler == null) { Bridge bridge = getBridge(); if (bridge == null) { return null; } ThingHandler handler = bridge.getHandler(); if (handler instanceof LutronMQTTHubHandler) { this.hubHandler = (LutronMQTTHubHandler) handler; this.hubHandler.registerDeviceStatusListener(this); } else { return null; } } return this.hubHandler; } protected void scheduleUpdateForDevice(int objectId) { log.info("Scheduling an update request for deviceId=" + objectId); scheduler.submit(new Runnable() { @Override public void run() { LutronMQTTHubHandler handler = getHubHandler(); if (handler != null) { onDeviceStateChanged(handler.getDeviceByObjectId(objectId)); } } }); } public LutronDevice getDevice() { if (device != null) { return device; } LutronMQTTHubHandler handler = getHubHandler(); device = handler.getDeviceByObjectId(objectId); return device; } @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatus) { super.bridgeStatusChanged(bridgeStatus); if (bridgeStatus.getStatus() == ThingStatus.ONLINE) { scheduleUpdateForDevice(objectId); } } @Override public void channelLinked(ChannelUID channelUID) { if (this.getBridge().getStatus() == ThingStatus.ONLINE) { // TODO really only need 1 for each device, no matter the channelse. scheduleUpdateForDevice(objectId); } else { log.info("Channel Linked but hub is not online."); } } @Override public void onDeviceFound(LutronDevice d) { if (d.getObjectId() == objectId) { updateStatus(ThingStatus.ONLINE); onDeviceStateChanged(d); } } @Override public void onDeviceRemoved(LutronDevice d) { if (d.getObjectId() == objectId) { updateStatus(ThingStatus.OFFLINE); } } @Override public void onDeviceStateChanged(LutronDevice d) { log.trace("onDeviceStateChanged my id=" + objectId + ", update is for " + d.getObjectId() + ", name=" + d.getName()); if (d.getObjectId() != objectId) { return; } if (setAsIs) { log.warn("AS IS: " + d.getProperty(LUTRON_PROPERTY_LEVEL)); setAsIs = false; } log.debug("Go device status change for " + this.getThing().getLabel()); if (d.hasUpdatedProperties()) { log.info("Received notice of pending state change."); } if (false && device != null && d.getProperty(LUTRON_PROPERTY_LEVEL) == (device.getProperty(LUTRON_PROPERTY_LEVEL))) { log.info("Lutron Device: " + d.getName() + " Received State Changed but no difference"); return; } device = d; log.info("Lutron Device: " + d.getName() + " State Changed: " + d.getProperty(LUTRON_PROPERTY_LEVEL)); // TODO we should keep the previous state so that we don't send unnecessary updates. - PercentType percentType = LightStateConverter.toBrightnessPercentType(d); + PercentType percentType = LightStateConverter.toBrightnessPercentType(d, inverted); log.info("Lutron: " + d.getName() + " Light Level: " + percentType.intValue()); updateState(powerLevelChannelName, percentType); } protected Integer getCurrentLevel(LutronDevice light) { int brightness = light.getProperty(LUTRON_PROPERTY_LEVEL); return brightness; } }