diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java index 4842f337df36a..54ee305b3a0bb 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java @@ -18,12 +18,30 @@ import java.util.List; import java.util.Map; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaDevice; import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaState; import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaStatus; -import org.openhab.core.library.types.*; -import org.openhab.core.thing.*; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +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.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +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.ThingStatusInfo; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.builder.ChannelBuilder; import org.openhab.core.thing.binding.builder.ThingBuilder; @@ -38,6 +56,7 @@ * The {@link SomfyTahomaBaseThingHandler} is base thing handler for all things. * * @author Ondrej Pecta - Initial contribution + * @author Laurent Garnier - Setting of channels at init + UoM for channels */ @NonNullByDefault public abstract class SomfyTahomaBaseThingHandler extends BaseThingHandler { @@ -46,23 +65,57 @@ public abstract class SomfyTahomaBaseThingHandler extends BaseThingHandler { private HashMap typeTable = new HashMap<>(); protected HashMap stateNames = new HashMap<>(); + protected String url = ""; + + private Map> units = new HashMap<>(); + public SomfyTahomaBaseThingHandler(Thing thing) { super(thing); + // Define default units + units.put("Number:Temperature", SIUnits.CELSIUS); + units.put("Number:Energy", Units.WATT_HOUR); + units.put("Number:Illuminance", Units.LUX); + units.put("Number:Dimensionless", Units.PERCENT); } public HashMap getStateNames() { return stateNames; } - protected String url = ""; - @Override public void initialize() { - url = getURL(); - if (getThing().getProperties().containsKey(RSSI_LEVEL_STATE)) { - createRSSIChannel(); + Bridge bridge = getBridge(); + initializeThing(bridge != null ? bridge.getStatus() : null); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + initializeThing(bridgeStatusInfo.getStatus()); + } + + public void initializeThing(@Nullable ThingStatus bridgeStatus) { + SomfyTahomaBridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler != null && bridgeStatus != null) { + url = getURL(); + if (getThing().getProperties().containsKey(RSSI_LEVEL_STATE)) { + createRSSIChannel(); + } + if (bridgeStatus == ThingStatus.ONLINE) { + SomfyTahomaDevice device = bridgeHandler.getCachedDevice(url); + if (device != null) { + updateUnits(device.getAttributes()); + List states = device.getStates(); + updateThingStatus(states); + updateThingChannels(states); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, UNAVAILABLE); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); } - updateStatus(ThingStatus.ONLINE); } private void createRSSIChannel() { @@ -178,11 +231,49 @@ protected void cacheStateType(String stateName, int type) { } } + private void updateUnits(List attributes) { + for (SomfyTahomaState attr : attributes) { + if ("core:MeasuredValueType".equals(attr.getName()) && attr.getType() == TYPE_STRING) { + switch ((String) attr.getValue()) { + case "core:TemperatureInCelcius": + case "core:TemperatureInCelsius": + units.put("Number:Temperature", SIUnits.CELSIUS); + break; + case "core:TemperatureInKelvin": + units.put("Number:Temperature", Units.KELVIN); + break; + case "core:TemperatureInFahrenheit": + units.put("Number:Temperature", ImperialUnits.FAHRENHEIT); + break; + case "core:RelativeValueInPercentage": + units.put("Number:Dimensionless", Units.PERCENT); + break; + case "core:LuminanceInLux": + units.put("Number:Illuminance", Units.LUX); + break; + case "core:ElectricalEnergyInWh": + units.put("Number:Energy", Units.WATT_HOUR); + break; + case "core:ElectricalEnergyInKWh": + units.put("Number:Energy", Units.KILOWATT_HOUR); + break; + case "core:ElectricalEnergyInMWh": + units.put("Number:Energy", Units.MEGAWATT_HOUR); + break; + default: + logger.warn("Unhandled value \"{}\" for attribute \"core:MeasuredValueType\"", attr.getValue()); + break; + } + break; + } + } + } + protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) { return parseTahomaState(null, state); } - protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) { + protected @Nullable State parseTahomaState(@Nullable String acceptedItemType, @Nullable SomfyTahomaState state) { if (state == null) { return UnDefType.NULL; } @@ -205,14 +296,32 @@ protected void cacheStateType(String stateName, int type) { switch (type) { case TYPE_PERCENT: Double valPct = Double.parseDouble(state.getValue().toString()); + if (acceptedItemType != null && acceptedItemType.startsWith(CoreItemFactory.NUMBER + ":")) { + Unit unit = units.get(acceptedItemType); + if (unit != null) { + return new QuantityType<>(normalizePercent(valPct), unit); + } else { + logger.warn("Do not return a quantity for {} because the unit is unknown", + acceptedItemType); + } + } return new PercentType(normalizePercent(valPct)); case TYPE_DECIMAL: Double valDec = Double.parseDouble(state.getValue().toString()); + if (acceptedItemType != null && acceptedItemType.startsWith(CoreItemFactory.NUMBER + ":")) { + Unit unit = units.get(acceptedItemType); + if (unit != null) { + return new QuantityType<>(valDec, unit); + } else { + logger.warn("Do not return a quantity for {} because the unit is unknown", + acceptedItemType); + } + } return new DecimalType(valDec); case TYPE_STRING: case TYPE_BOOLEAN: String value = state.getValue().toString(); - if ("String".equals(acceptedState)) { + if ("String".equals(acceptedItemType)) { return new StringType(value); } else { return parseStringState(value); @@ -247,9 +356,11 @@ private State parseStringState(String value) { switch (value.toLowerCase()) { case "on": case "true": + case "active": return OnOffType.ON; case "off": case "false": + case "inactive": return OnOffType.OFF; case "notdetected": case "nopersoninside": diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java index c71acdfdc1439..cebc4cec948d1 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java @@ -17,7 +17,12 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; @@ -35,9 +40,25 @@ import org.eclipse.jetty.http.HttpMethod; import org.openhab.binding.somfytahoma.internal.config.SomfyTahomaConfig; import org.openhab.binding.somfytahoma.internal.discovery.SomfyTahomaItemDiscoveryService; -import org.openhab.binding.somfytahoma.internal.model.*; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaAction; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaActionGroup; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaApplyResponse; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaDevice; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaEvent; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaLoginResponse; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaRegisterEventsResponse; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaSetup; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaState; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaStatus; +import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaStatusResponse; +import org.openhab.core.cache.ExpiringCache; import org.openhab.core.io.net.http.HttpClientFactory; -import org.openhab.core.thing.*; +import org.openhab.core.thing.Bridge; +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.ThingStatusInfo; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; @@ -106,6 +127,9 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler { */ private String eventsId = ""; + private ExpiringCache> cachedDevices = new ExpiringCache<>(Duration.ofSeconds(30), + this::getDevices); + // Gson & parser private final Gson gson = new Gson(); @@ -331,6 +355,16 @@ public List getDevices() { return response != null ? List.of(response) : List.of(); } + public synchronized @Nullable SomfyTahomaDevice getCachedDevice(String url) { + List devices = cachedDevices.getValue(); + for (SomfyTahomaDevice device : devices) { + if (url.equals(device.getDeviceURL())) { + return device; + } + } + return null; + } + private void getTahomaUpdates() { logger.debug("Getting Tahoma Updates..."); if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) { diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaDevice.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaDevice.java index 2f4e26d9b97f6..8b69b6ea2737c 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaDevice.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaDevice.java @@ -22,6 +22,7 @@ * to TahomaLink account. * * @author Ondrej Pecta - Initial contribution + * @author Laurent Garnier - Add attributes data */ @NonNullByDefault public class SomfyTahomaDevice { @@ -33,6 +34,7 @@ public class SomfyTahomaDevice { private String oid = ""; private SomfyTahomaDeviceDefinition definition = new SomfyTahomaDeviceDefinition(); private List states = new ArrayList<>(); + private List attributes = new ArrayList<>(); public String getLabel() { return label; @@ -61,4 +63,8 @@ public SomfyTahomaDeviceDefinition getDefinition() { public List getStates() { return states; } + + public List getAttributes() { + return attributes; + } }